['1','2','3'].map(parseInt)が[1, NaN, NaN]になる理由
Array.prototype.mapはコールバックに(要素, インデックス, 配列)の3引数を渡します。parseIntの第2引数は基数(何進数か)を表すため、インデックスがそのまま基数として渡されてしまいます。
なぜエラーが出ないのか
出力: [ 1, NaN, NaN ]
(エラーなし)- JavaScriptは何も報告しません。文法として正しいためです
出力: [ 1, NaN, NaN ]- 実際の挙動 — 期待した結果と食い違っている箇所
見つけ方- エラーが出ないので、出力を目で確かめるか、コードを目で追うしかありません。この種の誤りが最も発見が遅れます
このエラーが出る典型パターン
パターン1
1 const digits = ["1", "2", "3"]; 2 console.log(digits.map(parseInt )); ^
mapはコールバックに(要素, インデックス, 配列)を渡します。parseIntの第2引数は基数のため、インデックス1・2がそのまま基数として渡され、変換に失敗します。
直し方: parseInt を (v) => parseInt(v, 10) にします。
パターン2
1 const values = ["10", "20", "30"]; 2 console.log(values.map(parseInt )); ^
インデックス1が基数2、インデックス2が基数3として渡されるため、"20"や"30"は2進数・3進数としては変換できずNaNになります。
直し方: parseInt を (v) => parseInt(v, 10) にします。
パターン3
1 const values = ["4", "8", "12"]; 2 console.log(values.map(Number.parseInt)); ^
Number.parseIntも同じく第2引数を基数として扱うため、mapのインデックスをそのまま渡すと壊れます。Numberは余分な引数を無視するため安全です。
直し方: Number.parseInt を Number にします。
パターン4
1 const raw = ["5", "6", "7", "8"]; 2 console.log(raw.map(parseInt )); ^
配列の要素数が増えても仕組みは同じです。インデックスがそのまま基数として渡されます。
直し方: parseInt を (v) => parseInt(v, 10) にします。
パターン5
1 const codes = ["9", "10", "11"]; 2 console.log(codes.map(Number.parseInt)); ^
Number.parseIntも同じ罠にかかります。余分な引数を無視するNumberを使うのが安全です。
直し方: Number.parseInt を Number にします。
よくある誤解
parseIntを直接mapに渡せば数値に変換できると思いがちですが、mapが渡す3引数のうち2番目(インデックス)をparseIntは基数として解釈します。1番目の要素以降、意図しない基数で変換されます。
実務での勘所
同じ種類の事故は、parseInt以外の「複数の引数を受け取り、2番目以降の意味が呼び出し側の想定と違う」関数をmapに直接渡すたびに起こり得ます。安全な習慣は、関数を直接渡さずに.map(x => parseInt(x))のようにアロー関数で包み、mapが渡す引数のうち本当に使いたいものだけを明示的に選んで中の関数に渡すことです。この1手間を惜しまなければ、渡した関数が実は複数引数を受け取る仕様だったという事故を防げます。