JavaScriptのNaNは自分自身と一致しない|割引後の金額チェックが、ずっとすり抜けていた理由
注文フォームの金額チェックで、明らかにおかしい数字が素通りしていた。
NaNは自分自身とすら一致しない、という仕様に気づいていなかった。
注文フォームの金額チェック処理に、ずっと素通りしていた一行があった。
合計金額がおかしな値になっていないかを確認する場所で、if (total === NaN) と書いていた。
見た目はいかにも動きそうなのに、この条件は永遠にtrueにならない。
気づいたきっかけは、割引コードの入力欄を空のまま送信したときの動作だった。
合計金額の表示が「NaN円」になっているのに、エラーメッセージは一切出ていなかった。
チェックのコードを疑って初めて、書き方そのものが間違っていたと分かった。
NaNは「数値ではない」のに、型は数値だった
NaNは Not a Number の略で、名前だけ見ると数値の仲間はずれのように思える。typeof NaN は "number" を返す。
数値として扱われる場所に、計算できなかったという結果だけが入っている状態だった。
自分自身と比べても、一致しないという仕様
JavaScriptの数値の等価比較には、IEEE 754という規格の癖がそのまま乗っている。
この規格でNaN同士の比較は常にfalseと定められていて、NaN === NaN も NaN == NaN もfalseになる。
僕が書いた total === NaN は、totalが本当にNaNだったとしても、絶対にtrueにならない一文だった。
知識として知っていたはずなのに、自分のコードで踏むまで気づかなかった。
「値がNaNかどうか」を確かめたい場面で、「NaNと等しいか」という発想そのものが最初から成立していなかった。
判定には専用の関数を使う
NaNかどうかを調べるには、Number.isNaN()を使う。
よく似たisNaN()という古い関数もあるが、こちらは判定の前に値を数値へ変換しようとする癖があり、文字列の"abc"のような値までtrueにしてしまう。
数値であることが分かっている変数の中身だけを見たいなら、Number.isNaN()のほうが素直に動く。
今は入力の時点で止めている
合計金額のチェックは if (Number.isNaN(total)) に書き換えた。
ただそれだけでは根本の対策にならないと気づき、割引コードの入力欄で数値に変換できなかった時点でエラーを出すよう、チェックの位置そのものを前へ動かした。
壊れた値を後から見つけるより、壊れた値を作らせないほうが結局早い。
ほかにも書いています
note・スタンプ※noteは運営者が個人で書いているものです。
