【JavaScript】 NaNって何だって話とisNaNについて
更新日:2021/09/28
JavaScriptには NaNという値があります。
数値に関する値のようですが、少しわかり難い面があります。
そこでここでは NaNについて少し掘り下げてみます。
NaNとは
NaNとはJavaScript固有のものではなくて、 IEEE 754 浮動小数点規格で制定されている値で、Not-a-Number を略したものです。
数値同士を想定した演算や、数値を引数として受け取り何らかの結果を返すメソッドなどで、『対象となる値が数値ではないから処理できなかったよ』という結果を表す値として使用することを想定しています。
結果が NaNになる例
console.log( parseInt( "abc") ); // NaN
console.log( 100 * "abc" ); // NaN
また、結果が数値であらわせないとき NaNを返すことも想定しています。
例えばMath.sqrt( )は、引数の平方根を返します。
引数に負数が指定されると、数値ではあらわせないため結果がNaNになります。
結果が数値であらわせないとき NaN
console.log( Math.sqrt( -1 ) ); // NaN
NaNは”数値ではない”という結果をあらわすため、他の値と比較するような性質のものではありません。
そのため、比較した際に内部でエラーを発生させるなどの処理が考えられますが、JavaScriptでは false を返します。
NaNとの比較は全てfalse
console.log( 1 === NaN ); // false
console.log( 100 > NaN ); // false
console.log( "abc" > NaN ); // false
最も注意すべき点は、NaN同士の比較も false になる点です。
NaN同士の比較も false
const nan1 = NaN;
const nan2 = NaN;
// 変数の値は NaN
console.log( nan1 ); // NaN
console.log( nan2 ); // NaN
// でも比較するとfalse
console.log( nan1 == nan2 ); // false
console.log( nan1 === nan2 ); // false
やってしまいがちなので、注意しましょう。
JavaScriptのNaNはグローバルオブジェクトのプロパティ値
NaN値は、グローバルオブジェクトのNaNプロパティが返す値です。
プログラムコードで入力された数値や文字列は、対応するデータ値(プリミティブ)に変換されます。
プリミティブへの変換
const num = 12345; // ← 12345 という値の数値データに変換される
const text = "abcde"; // ← "abcde" という値の文字データに変換される
nullも同様に、null値に変換されます。
nullの変換
const nullValue = null; // ← nullデータに変換される
しかし NaN は、グローバルオブジェクトのプロパティが参照されます。
次のコードの各行は、同じものを参照しています。
NaNの参照
const nan1 = NaN;
const nan2 = globalThis.NaN;
ただし前項でお伝えしたように、取得した値同士の比較は必ずfalseになるので、注意が必要です。
NaNの比較は必ずfalse
console.log( nan1 === nan2 ); // false
console.log( NaN === NaN ); // false
console.log( NaN === globalThis.NaN ); // false
また理論上は、NaNの値を変更できます。
理論上のNaN値変更
globalThis.NaN = "abcde";
const nan1 = NaN;
console.log( na1 ); // "abcde"
実際には上書き禁止になっているため、変更できません。
非strictモードは、そのまま処理が続行しますが、strictモードでは、変更時にエラーが発生します。
NaNは上書き禁止です
"use strict";
globalThis.NaN = "abcde";
// TypeError: "NaN" is read-only
NaNの判定
結果がNaNかどうかを判定したいとき、次のコードのように直接 NaN と比較することができません。
NaNの判定(失敗)
const value = 123 * "abc";
console.log( value ); // NaN
if( value === NaN ){ // NaNとの比較結果は必ず false
console.log( "結果がNaNです" );
}
値がNaNかどうかを確認するには、isNaNメソッドを使用します。
二つのisNaNメソッド
JavaScriptにはisNaNメソッドが二つ存在します。
一つがグローバルオブジェクトのメソッド。
もう一つが、Numberオブジェクトのメソッドです。
Numberオブジェクトもグローバルオブジェクトのプロパティなので、イメージ的には次のような関係になります。
グローバルオブジェクト ┃ ┣ NaNプロパティ ┃ ┣ isNaNメソッド ┃ ┣ Numberオブジェクト ┃ ┣ isNaNメソッド ┃ ┃
この二つのメソッドは、名前が同じですがチェックしている内容が異なります。
そのため、内容を理解して適切に使い分ける必要があります。
グローバルオブジェクトのisNaNメソッドは、NaN値かどうかのチェックに向いていません。
NaN値かどうかのチェックは、NumberオブジェクトのisNaNメソッドを使用します。
isNaNメソッド
グローバルオブジェクトのisNaNメソッドは、引数を一つ受け付けます。
こメソッドは、名前を見ると引数が NaN値かどうかを判定しているような印象を受けます。
実際は、NaNの元々の意味である、Not-a-Number、つまり数字として扱える値かどうかを判定しています。
JavaScriptでは四則演算などで、数値以外も数値としてみなすことがあります。
例えば、1 - true は trueが1とみなされて、結果は 0 です。
しかし、1 + "abc" の場合、"abc"を数値に変換できません。
数値と数値でないものを計算した結果は、”数値でないもの” すなわち NaN値 となります。
グローバルオブジェクトのisNaNメソッドは、"abc"のような数値に変換できない値のときtrueを返します。
数値としてみなされる値については、次の記事の最初の項目を読んでみてください。
■【JavaScript】 数値チェック方法をケース別に解説します
isNaNによるNot-a-Number判定
console.log( isNaN( 12345 ) ); // false
console.log( isNaN( "12345" ) ); // false
console.log( isNaN( "abcde" ) ); // true
console.log( isNaN( true ) ); // false
console.log( isNaN( undefined ) ); // true
console.log( isNaN( null ) ); // false
console.log( isNaN( NaN ) ); // true
console.log( isNaN( Infinity ) ); // false
console.log( isNaN( {} ) ); // true
グローバルオブジェクトのisNaNメソッドは内部的には、最初に引数として受け取った値を数値に変換します。
このとき数値に変換できなかったものは、変換結果が NaN値 になり、isNaNの実行結果として true を返します。
引数と結果の関係を表にまとめると、次のようになります。
値の型 | 例 | ⇒ | 数値変換結果 | isNaN判定 | |
---|---|---|---|---|---|
Number | 有限値 | 1234 | ⇒ | 1234 | false |
Infinity | Infinity | ⇒ | Infinity | false | |
NaN | NaN | ⇒ | NaN | true | |
String | 数値文字列 | "1234" | ⇒ | 1234 | false |
非数値文字列 | "abcde" | ⇒ | NaN | true | |
Null | null | ⇒ | 0 | false | |
Undefined | undefined | ⇒ | NaN | true | |
Boolean | true | true | ⇒ | 1 | false |
false | false | ⇒ | 0 | false | |
Symbol | Symbol() | ⇒ | TypeError例外 | ||
BigInt | 100n | ⇒ | TypeError例外 | ||
Object | 数値プリミティブに変換できる | false | |||
数値プリミティブに変換できない | true |
■数値プリミティブに変換できるオブジェクト
一般的なオブジェクトは数値プリミティブに変換できませんが、[Symbol.toPrimitive]プロパティを実装することで数値プリミティブに変換できるようになります。詳しくは次のページをご覧ください。
■【JavaScript】 オブジェクトで直接計算させるSymbol.toPrimitiveでプリミティブ変換
Number.isNaNメソッド
NumberオブジェクトのisNaNメソッドは、受け取った引数が NaN値のとき、trueを返します。
Number.isNaNによるNaN判定
console.log( Number.isNaN( 12345 ) ); // false
console.log( Number.isNaN( "12345" ) ); // false
console.log( Number.isNaN( "abcde" ) ); // false
console.log( Number.isNaN( true ) ); // false
console.log( Number.isNaN( undefined ) ); // false
console.log( Number.isNaN( null ) ); // false
console.log( Number.isNaN( NaN ) ); // true
console.log( Number.isNaN( Infinity ) ); // false
console.log( Number.isNaN( {} ) ); // false
演算子による演算結果や関数の実行結果が NaNかどうかを確認する場合、Number.isNaNを使用します。
演算結果等が NaNかどうかのチェック
const checkNaN = value =>
console.log( Number.isNaN( value ) ? "NaNです" : "NaNではない" );
const value1 = 100 * 100;
const value2 = 100 * "abcde";
checkNaN( value1 );
checkNaN( value2 );
const intVal = parseInt( "abcdefg" );
checkNaN( intVal );
比較による NaN判定
NaN値は、自分自身を比較しても false になるという奇妙な性質があります。
その特徴を活かして、次のように比較することで NaN かどうかを確認できます。
NaNの判定
const value = 123 * "abc";
if( value !== value ){
console.log( "結果がNaNです" );
}
この項の最初の例では、等価比較( === )をおこなっていました。
ここでは、不等価比較( !== )をおこなっています。
NaN以外の値は自分自身を比較するとtrueになるので、このようなチェック方法も有効です。
しかし今後のECMAScriptの改版で、同じ性質を持つ値が追加される可能性はゼロではありません。
それ以前に、演算の目的がわかり難く、コメント必須です。
何らかの理由がないなら、Number.isNaNを使用することをおススメします。
更新日:2021/09/28
関連記事
スポンサーリンク
記事の内容について
こんにちはけーちゃんです。
説明するのって難しいですね。
「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。
裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。
掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。
ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php
このサイトは、リンクフリーです。大歓迎です。