中級のJavaScriptエラー 36問
12種類のエラーを、それぞれ複数パターンずつ収録しています。
- NaNを===やswitchで判定できない理由と直し方NaNはIEEE 754の仕様上、自分自身とも等しくないため、===はもちろん==でも決してtrueになりません。数値変換の失敗をチェックする際にこの性質を知らないと、判定が常にすり抜けます。
- メソッドを変数に代入・受け渡しするとthisが失われるオブジェクトのメソッドを変数に代入したり、別の関数に渡して呼び出したりすると、呼び出し時にレシーバーの情報が失われ、thisがオブジェクトを指さなくなります。
- ['1','2','3'].map(parseInt)が[1, NaN, NaN]になる理由Array.prototype.mapはコールバックに(要素, インデックス, 配列)の3引数を渡します。parseIntの第2引数は基数(何進数か)を表すため、インデックスがそのまま基数として渡されてしまいます。
- スプレッド構文{ ...obj }はネストした値までは複製しない(浅いコピー){ ...obj }や[...arr]は最上位のプロパティだけをコピーします。値がオブジェクトや配列の場合、コピー先とコピー元は同じ参照を共有したままです。
- Array.prototype.sort()が数値を文字列として並べ替える理由比較関数を渡さないsort()は、要素を文字列に変換してUnicodeコードポイント順に並べ替えます。数値の大小とは無関係な結果になります。
- 0.1 + 0.2 === 0.3 がfalseになる理由と直し方浮動小数点数は2進数で表現されるため、0.1や0.2のような10進の小数を正確に表せません。演算結果はごくわずかな誤差を含み、===での比較は失敗します。
- typeof null === 'object' の罠typeof演算子はnullに対して"object"を返します。これは言語仕様初期からのバグ扱いの挙動で、修正すると既存コードが壊れるため現在も残っています。オブジェクトかどうかの判定にtypeofだけを使うとnullをすり抜けさせてしまいます。
- forループのvarをletに変えないとコールバックが同じ値を指すvarはブロックスコープを持たないため、ループ内で生成した関数はすべて同じ変数を共有します。ループが終わったあとに関数を呼び出すと、全て最後の値を参照します。letはループの繰り返しごとに新しい束縛を作るため、この問題が起きません。
- 同じ内容のオブジェクト同士が === でfalseになる理由オブジェクトや配列の==・===は中身ではなく参照(メモリ上の同一性)を比較します。プロパティの値がすべて同じでも、別々に作られたオブジェクトは等しいと判定されません。
- 配列の分割代入で要素が足りないとundefinedになる(デフォルト値の付け方)配列の分割代入で、元の配列より多い変数を用意すると、対応する要素が無い変数はundefinedになります。各変数にデフォルト値を指定することでこれを防げます。
- JSON.parse()が投げるSyntaxErrorは実行時エラーであるJSON.parse()に不正な形式の文字列を渡すとSyntaxErrorを投げますが、これはJavaScript自体の構文解析エラーとは異なり、関数呼び出しの結果として実行時に発生する例外です。try/catchで捕まえられます。
- オプショナルチェイニング(?.)でnull/undefinedアクセスのエラーを防ぐネストしたプロパティの途中がnullやundefinedかもしれない場合、通常のドットアクセスではTypeErrorになります。?.を使うと、途中がnull/undefinedのときに例外を投げず、undefinedを返して処理を続けられます。