NaN をなんとかしたい上での諸注意 と global.isFinite() の罠

  • 59
    Like
  • 2
    Comment
More than 1 year has passed since last update.

きっかけは JavaScript NaN type is crazy にコメントした事ですが

1. NaN === NaN は false

typeof NaN // 'number'
typeof Inifity // 'number'
typeof -Infinity // 'number'
toString.call(NaN); // '[object Number]'

Infinity == Infinity // true
Infinity == -Infinity // false
Infinity == NaN // false
NaN == NaN // false
NaN === NaN // false (!!)

みんな知ってるね

2. global.isNaN("NaN") は true, global.isFinite("0") も true

で、このNaNInfinity が厄介である為に isNaN()isFinite() がグローバルオブジェクトに用意されているのですが、これもまた罠があります

isNaN(1);  // false
isNaN(NaN); // true 
isNaN("NaN"); // true (!?)

グローバルオブジェクトの isNaN()isFinite() は引数が文字列でも数値型にパースできるもんだとみなして判定に入ります。これ、isFinite() の方が厄介で、ここで無限大ではない(ちゃんと数値演算できる)変数だと判定してそのまま演算してしまうドジを踏みうる。

var test = function(x) {
  if (isFinite(x)) {
    return x + x;
  }
};

test('10'); // "1010"

3. ちゃんと使おう Number.isNaN(), Number.isFinite()

厳格な数値の NaN 判定、あるいは有限数判定にはグローバルメソッドではない Number.isNaN()Number.isFinite() を使いましょう。

isNaN(NaN); // true
isNaN('NaN'); // true
Number.isNaN(NaN); // true
Number.isNaN('NaN'); // false

isFinite(10); // true
isFinite('10'); // true
Number.isFinite(10); // true
Number.isFinite('10'); // false

if (typeof x === 'number') { ... } のようなバリデーションより手っ取り早く、かつ安心ですよ。

4. (追記)「それ IE とかじゃ Number.isNaN() とかないじゃないですかー」「それくらい Polyfill りなさいよ」

現状、IE 以外のブラウザは ECMAScript 6 の仕様にもかかわらず先行実装してくれているけど、 IE 9 以前のブラウザには用意されていません。でも、以下のようなコードで polyfill することはできます。

Number.isFinite = Number.isFinite || function(any) {
  return typeof any === 'number' && isFinite(any);
};

Number.isNaN = Number.isNaN || function(any) {
  return typeof any === 'number' && isNaN(any);
};