Home

Allajah's Reservoir

15 Dec 2017

JavaScript の NaN について

このエントリーをはてなブックマークに追加

この記事は#kosen10s Advent Calendar 2017の15日目の記事です。

昨日は寒い日は紅茶に砂糖を入れて飲むとおいしい - 死後裁きにあうでした。 美味しい紅茶が飲みたくなりました。

2つとった枠のうち1つは技術系の記事を書こうと思ってたのですが、準備不足もあり小ネタです。

JavaScriptでコードを書いていると、まれにNaN(Not a Number)に遭遇することがあります。 別に知ってれば大したことないのですが、若干ややこしくて厄介なのでNaNについて解説したいと思います。

どういうときに遭遇するか

NaNはMath objectの関数に引数として不適な値を渡したり、parseInt()などで文字列を数値に変換させようとすると返ってくることがあります。

また、let x = NaNのように代入可能です。

例:

Math.sqrt(-1); // => NaN 虚数は返ってこない
parseInt('hello', 10) // => NaN

https://example.com?page=18というURLからpageパラメータを取得し、その値に応じて表示する内容を書き換える といったコードを書いた時、https://example.com?page=helloに対しては例外処理が必要です。

ちなみに、数値x(!==0)0で割ったときにはInfinityもしくは-Infinityとなり、0 / 0NaNとなります

Truthy, Falsy

NaNを単体で評価するとFalsyになります。直感的ですね。

NaN ? 'foo' : 'hoge'; // => 'hoge'

比較

NaNは比較演算子で評価した場合、どんな値とも等価にはなりません。 ここで気をつけなければいけないのが、NaN === NaNfalseになることです。

NaN === false // => false
NaN === 0 // => false
NaN > 0 // => false
NaN < 0 // => false
NaN === NaN // => false
NaN !== false // => true
NaN !== true // => true
NaN !== NaN // => true

NaNの検出

JavaScriptにはisNaN()というトップレベル関数が用意されています。引数を一つとり、boolean(true | false)を返します。 ただ、このisNaN()にも一癖あって、渡された引数がNaN以外にも、文字列、undefined、Object、 Functionだった場合もtrueを返します。
そのため、isNaN(x)によりtrueが返却されても、xNaNである保証はありません。

isNaN(NaN) // => true
isNaN(undefined) // => true
isNaN('hello') // => true
isNaN({}) // => true
isNaN(new Function) // => true
isNaN(0) // => false
isNaN(true) // => false
isNaN(null) // => false

引数がArrayの場合はlengthが0か、要素1つだけで値が数値またはnullの場合のみfalseが返ります

isNaN([1,2,3]) // => true
isNaN(['hello']) // => true
isNaN([true]) // => true
isNaN([]) // => false
isNaN([1]) // => false
isNaN([null]) // => false

非常にややこしいですね。覚えなくて大丈夫です。
というのも、ECMAScript2015でNumber.isNaN()が導入され、これを用いることによりNaNかどうかを正しく評価することができるようになりました。

Number.isNaN(NaN) // => true
Number.isNaN(undefined) // => false
Number.isNaN('hello') // => false
Number.isNaN({}) // => false
Number.isNaN(new Function) // => false
Number.isNaN(0) // => false
Number.isNaN(true) // => false
Number.isNaN(null) // => false

すばらしいですね。非常にわかりやすくなりました。とはいえ、前時代のJavaScriptを書かなければいけないこともあるかもしれません。 そのときは先に述べた、NaNは比較演算子で評価した場合、どんな値とも等価にはならないという性質を利用します。

Number.isNaN = function(val) {
  return val !== val;
}

Number.isNaN(NaN) // => true;
Number.isNaN(0) // => false;

まとめ

トップレベル関数のisNaN()は使ってはいけません。Number.isNaN()を使いましょう。

明日はruryushamさんの記事です。

Allajah at 15:44