JavaScriptの開発において、多くのエンジニアを悩ませる要素の一つに「NaN」の判定があります。
「Not-a-Number」の略称であるNaNは、数値型でありながら数値ではないという矛盾した性質を持っており、その判定には注意が必要です。
標準で用意されているisNaN関数と、ES6(ECMAScript 2015)で追加されたNumber.isNaNメソッドには、初心者が見落としがちな決定的な違いが存在します。
本記事では、これら二つの判定方法の仕組みを深掘りし、実務でバグを生まないための適切な使い分けと注意点を詳しく解説します。
NaNとはどのような値なのか
JavaScriptにおけるNaNは、算術演算の結果が定義されていない場合や、数値への変換に失敗した際に生成される特別な値です。
例えば、0を0で除算したり、不適切な文字列を数値に変換しようとしたりするとNaNが発生します。
NaNの最も奇妙な特徴は、自分自身を含め、いかなる値とも等しくないという点にあります。
以下のコードで、NaNの等価比較の結果を確認してみましょう。
// NaNの等価比較
console.log(NaN === NaN); // 自分自身との比較
console.log(NaN == NaN); // 緩やかな比較
false
false
このように、通常の比較演算子ではNaNを判定することができないため、専用の関数が必要となります。
また、typeof演算子を使用すると、NaNの型は"number"として返されます。
「数値ではない」という意味を持ちながら「数値型」であるという点が、多くの混乱を招く原因となっています。
グローバル関数isNaNによる判定の仕組み
JavaScriptには古くから、グローバル関数としてisNaNが用意されています。
この関数は、引数がNaNであるかどうかを判定するために使用されますが、その内部的な挙動には注意が必要です。
isNaNは、引数をまず「数値」に変換しようと試みます。
変換の結果がNaNになれば、その引数が元々NaNでなくてもtrueを返してしまいます。
// グローバルisNaNの挙動
console.log(isNaN(NaN)); // true
console.log(isNaN("hello")); // 文字列だが数値に変換できないためtrue
console.log(isNaN(undefined));// 数値に変換するとNaNになるためtrue
console.log(isNaN({})); // オブジェクトもNaN判定されるためtrue
true
true
true
true
このように、数値型ではないデータに対してもtrueを返してしまう性質は、厳密なデータチェックを行いたい場合にバグの温床となります。
Number.isNaNによる厳密な判定
ES6で導入されたNumber.isNaNは、グローバル関数の欠点を補うために設計されました。
このメソッドは、「型が数値であり、かつ値がNaNであること」を厳密にチェックします。
暗黙的な型変換を行わないため、予期せぬ結果を招く心配がありません。
// Number.isNaNの挙動
console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN("hello")); // 型が文字列なのでfalse
console.log(Number.isNaN(undefined));// 型がundefinedなのでfalse
console.log(Number.isNaN(123)); // 通常の数値なのでfalse
true
false
false
false
モダンなJavaScript開発においては、ほとんどのケースでNumber.isNaNを使用することが推奨されます。
なぜ挙動に違いがあるのか
グローバル関数は、JavaScriptの初期設計における「緩やかな型変換」の思想に基づいています。
一方で、Numberオブジェクトのメソッドとして定義された新しい関数群は、より静的で予測可能な挙動を重視しています。
この設計の差を理解しておくことで、古いコードのメンテナンス時にも混乱を防ぐことができます。
二つの関数の比較と使い分け
判定の対象となるデータによって、どちらの関数を使うべきかが変わります。
基本的には、入力値の型が保証されていない場合や、厳密な数値計算の結果を検証したい場合にはNumber.isNaNを選択してください。
逆に、入力値が文字列であっても「それが数値として解釈可能かどうか」を調べたい文脈であれば、あえてグローバルなisNaNを利用することもあります。
ただし、その場合でもNumber(value)などで明示的に型変換を行った後に判定する方が、コードの意図が明確になります。
各種データ型に対する判定結果の比較
以下の表は、代表的な値に対して二つの関数がどのような結果を返すかをまとめたものです。
| 入力値 (value) | isNaN(value) の結果 | Number.isNaN(value) の結果 |
|---|---|---|
NaN | true | true |
"123" | false | false |
"abc" | true | false |
undefined | true | false |
null | false | false |
true | false | false |
{ } | true | false |
この表から分かる通り、Number.isNaNは「本当にNaNであるとき」のみtrueを返す非常に安全なメソッドです。
実務で遭遇するNaNの罠と対策
実際の開発現場では、APIからのレスポンスやユーザーの入力値を計算に用いる際にNaNが入り込むことがあります。
ここでは、特に注意すべきケースと、その回避策について解説します。
計算過程での伝播
NaNには「一度計算に混じると、その後の計算結果もすべてNaNにする」という性質があります。
これを「NaNの伝播」と呼び、エラーの発生源を特定しにくくする要因となります。
const price = "時価";
const taxRate = 0.1;
const total = parseFloat(price) * (1 + taxRate);
console.log(total); // NaN
NaN
このような事態を防ぐためには、計算の各ステップ、あるいは入力値を受け取った直後にバリデーションを実施することが不可欠です。
配列操作におけるNaN
配列の中にNaNが含まれている場合、検索メソッドによって挙動が異なる点にも注意が必要です。
indexOfメソッドは内部で厳密等価演算(===)を使用するため、NaNを見つけることができません。
一方で、ES2016で導入されたincludesメソッドはNaNを正しく認識できるように設計されています。
const arr = [10, NaN, 20];
console.log(arr.indexOf(NaN)); // -1 (見つからない)
console.log(arr.includes(NaN)); // true (見つかる)
-1
true
リストの中に不正な計算結果が含まれていないかを確認する際には、includesやsomeメソッドを活用しましょう。
Object.isによる判定
NaNを判定するもう一つの方法として、Object.isメソッドがあります。
Object.isは二つの値が同一であるかを判定するメソッドですが、===とは異なり、NaN同士を等しいとみなします。
// Object.isによる判定
console.log(Object.is(NaN, NaN)); // true
true
特定の変数がNaNであるかをピンポイントで確認したい場合には、このメソッドも有効な選択肢となります。
モダンJavaScriptにおけるベストプラクティス
2026年現在の開発標準において、私たちが守るべきNaN判定のルールを整理します。
第一に、可能な限りNumber.isNaNを第一選択とすることです。
古いブラウザ(Internet Explorerなど)への対応が必要ない環境であれば、グローバルなisNaNを使用する理由はほとんどありません。
第二に、数値変換を伴う処理では、変換後に即座にチェックを行う習慣をつけることです。
Number()やparseInt()の結果をそのまま次の処理に渡すと、NaNがプログラム全体に拡散してしまいます。
第三に、TypeScriptなどの静的型付けツールを併用している場合でも、実行時の型チェックとしてNaN判定を省略しないことです。
型定義上はnumberであっても、実際の値がNaNになる可能性は常に排除できないからです。
自作の判定関数を作成する場合
もし、非常に古い環境や特殊なランタイムでNumber.isNaNが使えない場合は、NaNの「自分自身と等しくない」という性質を利用したポリフィルが使われます。
// Number.isNaNのポリフィル例
function myIsNaN(value) {
return typeof value === 'number' && value !== value;
}
このように、値が数値であり、かつ自分自身と異なる場合にのみtrueを返すロジックを書くことで、厳密な判定を再現できます。
まとめ
JavaScriptにおけるNaNの判定は、一見単純に見えて非常に奥が深いテーマです。
グローバル関数のisNaNは、便利な反面、暗黙的な型変換によって意図しないtrueを返してしまうリスクを抱えています。
一方で、Number.isNaNは型チェックを含めた厳密な判定を行うため、堅牢なコードを書くためには欠かせない存在です。
それぞれの特性を正しく理解し、適切な場面で適切なメソッドを選択することが、バグの少ない高品質なプログラムへの第一歩となります。
数値計算やユーザー入力の処理を記述する際は、本記事で紹介した使い分けをぜひ意識してみてください。
