【JavaScript】NaNとは?数値判定や動作の仕組みを初心者向けにわかりやすく解説

はじめに

皆さんはJavaScriptで計算を行っているときに、NaN という値を見かけたことはないでしょうか。

このNaNは「Not a Number(数値ではない)」の頭文字を取った略称ですが、実際に使ってみると少し不思議な動作をします。

たとえば「NaN同士を比較したらどうなるのか」「文字列と数値を足し算するとどうなるのか」など、最初は戸惑いやすい部分が多いかもしれません。

そこでこの記事では、初心者の方でも理解できるようにNaNとは何かを丁寧に解説します。

フォーム入力のバリデーションや外部サービスから受け取ったデータの処理など、実務シーンにつながる話題も取り上げますので、ぜひ参考にしてみてください。

この記事を読むとわかること

  • NaNの基本的な役割と動作の仕組み
  • NaNが発生する主なケースと実務での注意点
  • isNaNやNumber.isNaNなど、NaNを判定するための具体的な方法
  • フォーム入力やAPIレスポンスなど、実務シーンでのNaN活用例

NaNとは何か

NaNは「Not a Number」の略称で、JavaScriptで算術演算を行った際に数値として成立しない結果が発生すると返される特殊な値です。

多くの場合、数値型に変換できない文字列との演算や、未定義の変数を利用した計算で現れます。

以下の例は、文字列と数値を足し算しようとしてNaNが返ってくる典型的なケースです。

const result = "abc" - 1;
console.log(result); // NaN

JavaScriptの場合、"abc" という文字列を数値に自動変換しようと試みますが、変換できないためNaNが返ってきます。

こうした挙動に慣れていないと、「なぜ0やエラーにはならないの?」と疑問に思うかもしれません。

しかしJavaScriptの仕様上、計算結果が数値にならない場合にはNaNが返される、と理解しておくとスッキリします。

NaNは数値型

混乱しやすいポイントとして、NaNはJavaScriptにおける数値型の一つです。

文字通り「数値ではない」と言っていながら、型としては number に分類されます。

以下のコードで、typeof NaN の結果を確認できます。

console.log(typeof NaN); // "number"

NaNは「数値として扱えない値」を表す数値型、という矛盾を孕んでいるようにも見えますが、これは仕様として覚えておくと便利です。

この性質により、数値演算が関係する場面ではNaNを返却することで「無効な数値結果」を一意的に示してくれます。

NaNが発生する主な理由

NaNは数値演算が行われたとき、計算結果が数値として定義できない場合に返されます。

主な理由としては下記のようなものがあります。

1. 文字列と数値の演算

数値へ変換できない文字列を引き算や掛け算のような演算に使うとNaNが返されます。
例: "hello" * 2

2. 未定義の変数と演算

undefined な変数や初期化されていない変数を計算に使うとNaNが返されることがあります。

3. 数値変換エラー

強制的に parseIntparseFloat で数値に変換しようとしても、変換先の文字列が不適切な場合はNaNになる可能性があります。

4. 演算結果が数学的に不正な状態

一部の数値演算、たとえば 0 / 0 などのように定義されていない操作を行うとNaNが返ります。

NaNは単なるエラーというより、JavaScriptで計算結果が無効になったことを示す「マーカー」のように考えると理解しやすいです。

NaNの比較と等価性

NaNはほかのどの値とも等しくありません。

NaNはNaN同士でも等しくない という不思議な性質を持っています。

たとえば以下のコードを見てください。

console.log(NaN === NaN); // false
console.log(NaN == NaN);  // false

通常の数値であれば同じ値同士を比較した場合に true が返ってきます。

しかしNaNはどのように比較しても常に false になります。

なぜNaN同士を比較してもfalseになるのか

これはJavaScriptの仕様の一部で、「NaNはどんな値とも等しくない」というルールが決められているからです。

端的に言えば、「NaNは数値演算が成立しないという特異的な状態」を表すため、イコール演算子による比較が常に false になる仕様として設計されています。

この点を知らずに比較処理を書いてしまうと、バグの原因になりやすいので注意しましょう。

NaNを判定する方法

NaNを取り扱う際、「値がNaNであるかどうか」を判定する方法が非常に重要になります。

ここで代表的な手法として、isNaNNumber.isNaN の違いを確認してみましょう。

isNaN関数

グローバル関数の isNaN は、引数を強制的に数値へ変換した上でNaNかどうかを判定します。

console.log(isNaN("abc")); // true
console.log(isNaN(NaN));   // true
console.log(isNaN("123")); // false

一見便利に見えますが、"123" のように数値へ変換可能な文字列は false になり、"abc" のように変換できない文字列は数値型に変換した結果がNaNとなるため true になります。

しかしここで注意すべきは、数値に変換可能かどうかを間接的に判定しているために、意図しない結果を引き起こす場合があることです。

たとえば以下のようなケースがあります。

console.log(isNaN("100")); // false ("100" -> 100 -> NaNではない)
console.log(isNaN(true));  // false (true -> 1 -> NaNではない)

このように isNaN は「値がNaNかどうか」というよりも「数値へ変換できるかどうか」を判定する関数として働くことが多いと理解しておくのが安全です。

Number.isNaN関数

一方で Number.isNaN は、値が厳密にNaNであるかどうかを判定します。

引数を強制的に数値変換せず、そのまま比較を行うため、余計な型変換が行われません。

console.log(Number.isNaN("abc")); // false
console.log(Number.isNaN(NaN));   // true
console.log(Number.isNaN("123")); // false
console.log(Number.isNaN(true));  // false

結果を見てわかるように、"abc" は数値変換されずにそのまま判定されるため false が返ります。

もし純粋に「この値がNaNなのか」を確かめたいのであれば、Number.isNaN を使うのがおすすめです。

実務での使い分け

  • isNaN : フォーム入力など、まず文字列を数値に変換できるかチェックしたい場合に有用です。
  • Number.isNaN : すでに数値型であることが明確な値に対して「NaNかどうか」を厳密に判定したい場合に便利です。

ただしフォーム入力などでも、より意図通りの挙動を得たい場合は Number.isNaN(Number(value)) のように自分で数値変換を行った上で Number.isNaN を呼び出す方法もあります。

NaNと一緒に出現しやすいInfinity

NaNと同様に、JavaScriptの数値型の特殊値として Infinity-Infinity があります。

これらは数学的に無限を表す値であり、以下のような操作を行うと出現します。

console.log(1 / 0);   // Infinity
console.log(-1 / 0);  // -Infinity

Infinity同士を操作するとNaNになる場合があります。

たとえば Infinity - Infinity は、結果が定義できないためNaNになります。

console.log(Infinity - Infinity); // NaN

Infinity自体も、数値演算が破綻しそうな兆候を示す指標として捉えておくと、NaNと一緒にエラー処理やバリデーションなどで役立つでしょう。

実務シーンでのNaNの扱い方

NaNは実務でエラーを示す一つのサインとして活用できます。

具体的には、外部APIから受け取ったデータの数値項目が期待どおりではなかった場合、あるいはフォーム入力が数値に変換できないときなどに、NaNを用いたバリデーションを行うケースがよくあります。

フォーム入力のバリデーション例

ユーザーが金額を入力するフォームを想定して、NaNを使ってエラーチェックをしてみましょう。

function validateAmount(inputValue) {
  const amount = Number(inputValue);

  if (Number.isNaN(amount)) {
    return {
      valid: false,
      message: "入力が数値ではありません。数値を入力してください。"
    };
  }

  if (amount < 0) {
    return {
      valid: false,
      message: "金額は0以上である必要があります。"
    };
  }

  return {
    valid: true,
    message: "OK"
  };
}

// 使用例
const userInput = "abc";
const result = validateAmount(userInput);
console.log(result);

上記のように、入力値が数値に変換できなければ Number.isNaN(amount)true になり、適切なエラーメッセージを返すことができます。

この方法であれば、誤って truenull などの値が渡された場合でも、適切にNaNかどうか判定することが可能です。

外部APIからのデータを受け取るとき

外部APIからJSONなどで数値データを受け取り、アプリケーション内部で計算に使うことはよくあります。

しかしAPI仕様の変更や不具合などで、予期せぬ文字列や未定義の値が送られてくる可能性も考えられます。

こうした場合も、NaNであるかどうかを判定することで、バグが発生する前に対処がしやすくなります。

例として、在庫数を取得するAPIを呼び出し、その値がNaNになってしまうケースを想定してみます。

async function fetchInventory(apiUrl) {
  const response = await fetch(apiUrl);
  const data = await response.json();

  // ここで想定外のデータが送られてくることがある
  const inventory = Number(data.inventoryCount);

  if (Number.isNaN(inventory)) {
    // 在庫数が不明なので後続処理を止める
    throw new Error("在庫数が数値ではありません。APIのレスポンスを確認してください。");
  }

  return inventory;
}

このようにNaNチェックを挟んでおくと、「在庫数が実質的に存在しないのに0を扱ってしまう」というようなバグを未然に防げます。

NaNを回避するためのテクニック

NaNはJavaScript独自の概念ではなく、他言語の中でもよく似た仕組みが存在することがあります。

ただ、JavaScriptの場合は暗黙の型変換などが影響して、気がつかないうちにNaNが発生してしまうこともあります。

文字列から数値への変換ルールを把握する

文字列を数値に変換する際、先頭の空白は無視される など、細かいルールがいくつか存在します。

思わぬところでNaNを生むことがあるので、日頃から以下のような点を意識しましょう。

  1. 文字列から数値へ変換するときは必ず Number()parseInt(), parseFloat() を明示的に使う
  2. 意図しない暗黙的変換(演算子 + や比較演算で行われる変換)に注意する
  3. 変換後、Number.isNaN や範囲チェックなどを行い、問題がないかを確認する

短絡評価で未定義をブロックする

実務では、計算対象の値が「すでに存在しないかもしれない」という状況がある程度想定できます。

たとえば undefinednull が入る可能性があるとわかっているなら、短絡評価(論理演算子の || など)を使ってNaNの発生を抑える方法があります。

function safeAdd(a, b) {
  const numA = Number(a) || 0;
  const numB = Number(b) || 0;
  return numA + numB;
}

console.log(safeAdd(null, 5));      // 5
console.log(safeAdd(undefined, 10)); // 10
console.log(safeAdd("abc", 10));     // 10 (Number("abc") -> NaN, NaN || 0 -> 0)

この例では、暗黙にNaNになってしまうケースを「0で置き換える」動きにしています。

ただしこの手法は、NaNが発生していること自体を見逃す恐れもありますので、本当に問題を隠蔽して良い場面かどうか慎重に検討しましょう。

文字列表現とNaN

JavaScriptでは、文字列と数値を混ぜた演算において、加算演算子(+)は「文字列連結」を優先する一方、減算演算子(-)や乗算演算子(*)は「数値演算」を優先します。

そのため、文字列変換されるか、数値変換されるかで結果が変わってきます。

console.log("5" + 3);  // "53" (文字列連結)
console.log("5" - 3);  // 2   (数値演算)
console.log("abc" + 2); // "abc2"
console.log("abc" - 2); // NaN

このように、同じ演算子でも「文字列か数値か」を意識しないと、NaNを返してくる可能性があります。

NaNとオブジェクト、配列との関係

配列やオブジェクトを数値演算にかけた場合もNaNが生じることがあります。

たとえば、オブジェクトと数値を引き算しようとすると、[object Object] を数値に変換できずNaNが返ってきます。

const obj = {};
console.log(obj - 1); // NaN

配列の場合、配列が1要素の場合はその値を使って数値へ変換されるケースがありますが、それ以外だとNaNになりやすいです。

const arr1 = [10];
const arr2 = [10, 20];
console.log(arr1 - 5); // 5  ([10] -> "10" -> 10 - 5 -> 5)
console.log(arr2 - 5); // NaN

オブジェクトや配列を演算に使う機会はそう多くありませんが、うっかり -* を使ってしまうとNaNが返ってくるので注意しましょう。

NaNとJSONのやりとり

JSONではNaNは正式な数値ではないため、NaNを含むオブジェクトを直接JSONに変換すると、別の値(null)になってしまうことがあります。

const data = { value: NaN };
const jsonStr = JSON.stringify(data);
console.log(jsonStr); // {"value":null}

このように NaN は文字列化すると null に変換されてしまうため、サーバー側とやり取りする際には注意が必要です。

後で「null が来るはずなのにNaNだった」というような混乱を起こさないためにも、データをやり取りするときにNaNが出現しない設計を考慮しておくとトラブルを回避できます。

他の数値型とNaNの境界線

JavaScriptには浮動小数点数(いわゆる一般的な小数)と整数を区別する厳密な型はありません。

全てが number 型として統一されるため、InfinityNaN を含めて一種類の数値型の中でいろいろな特異値を扱う形となります。

C言語やJavaなど、別の言語経験がある方だと少し戸惑うかもしれません。

しかし慣れてしまうと、NaNによりエラー発生を素早く検知できるメリットもあると感じるでしょう。

開発効率向上のための注意点

NaNの存在によって、時折バグの原因が見えにくくなることもあります。

そのため、開発の際には以下のようなポイントを意識すると良いでしょう。

1. 厳格な比較を使う

NaNとの比較には通常の === ではなく、Number.isNaN を組み合わせる。

2. 暗黙の型変換を避ける

文字列連結を意図しているのか、数値演算を意図しているのか、コードを書くときに明示的に示すようにする。

3. 入力値は常にチェックする

フォーム入力やAPIレスポンスなど外部からデータが来る場合、必ず数値変換の結果を確認し、NaNならエラー処理を行う。

4. テストケースを増やす

テストの段階でNaNが出るようなパターンをあえて試すことで、予期しない不具合を早めに見つけられる。

5. デバッグ時のコンソール出力

計算前後で console.log() を挟み込み、値がNaNになっていないかチェックする習慣をつける。

NaNとTypeScriptの補足

TypeScriptなど型システムを導入している場合も、JavaScriptの仕様を受け継いでいるためNaNは存在します。

型定義を厳密にしておけば、NaNになる状況をコンパイラが警告してくれる場面も増えます。

ただし、完全にNaNを排除するのは難しいこともあるので、「NaNが発生しうる」という意識を持ってコードを設計することが大切です。

TypeScriptならではの型検証も活用しつつ、Number.isNaNでのチェックなどJavaScriptの基本仕様も合わせて押さえておくと安心です。

NaNが返るならエラーを投げてしまう?

アプリケーションの性質によっては、NaNが返る時点で即座にエラーをスローしてしまう設計が有効な場合もあります。

たとえば、金融計算や報告書作成など正確な数値が絶対に必要な現場では、中途半端にNaNを持ったまま後続の処理に進むと重大な問題を引き起こす恐れがあります。

function strictCalculate(value1, value2) {
  const num1 = Number(value1);
  const num2 = Number(value2);

  if (Number.isNaN(num1) || Number.isNaN(num2)) {
    throw new Error("計算できない値が含まれています。");
  }

  return num1 + num2;
}

このように早期にエラーを投げることで、不正なデータが入ってきたことをより明確に把握できるでしょう。

NaNを意識して開発するメリット

NaNを意識して開発することには、以下のようなメリットがあります。

無効な数値演算を素早く検知できる

0null などで上書きするよりも、NaNで返ってきた方が異常を見落としにくい。

デバッグしやすい

計算途中でNaNが混入すると、値の流れが一気に崩れるため調査対象が絞りやすい。

仕様が明確になる

「文字列はここで数値に変換」「ここでエラー処理を行う」など、プロジェクトのルール作りがはかどる。

逆にNaNへの理解が不十分だと、不具合や計算ミスを見逃してしまうリスクが高まります。

まとめ

NaNはJavaScriptで数値演算を行う上で必ず押さえておきたいポイントです。

数値に変換できない入力があったときに返される特殊な値であり、NaNはNaN同士でも等しくない という性質があります。

さらに、isNaNNumber.isNaN の違いを意識することで、誤ったバリデーションを避けられます。

実務で活用する際には、フォーム入力や外部APIのレスポンスなどでNaNが混入しうるケースに注意し、適切なエラー処理やバリデーションを実装すると安心です。

NaNをあえて早期発見のきっかけにすることで、バグの見逃しを減らすメリットも大いにあります。

今後JavaScriptで開発を進める際、ぜひNaNの特性を意識してコードを書いてみてください。

どのような経緯でNaNが発生するのか、あらかじめ仕様で定義しておくと開発チーム全体が混乱しにくくなるでしょう。

JavaScriptをマスターしよう

この記事で学んだJavaScriptの知識をさらに伸ばしませんか?
Udemyには、現場ですぐ使えるスキルを身につけられる実践的な講座が揃っています。