为什么 ['1','5','11'].map(parseInt) 会返回 [1, NaN, 3]

问题背景

在 JavaScript 中,使用 Array.prototype.map 方法时,parseInt 函数的行为可能会让人感到困惑。

示例代码:

['1', '5', '11'].map(parseInt);

该代码返回的结果是:

[1, NaN, 3]

以下是对此行为的详细分析。


解析过程

map 方法的执行机制

map 方法会对数组中的每个元素调用回调函数,并传入以下三个参数:

  1. 当前元素的值 (element)
  2. 当前元素的索引 (index)
  3. 原数组 (array)

调用 map(parseInt) 等价于:

['1', '5', '11'].map((element, index) => parseInt(element, index));

parseInt 的参数

parseInt 函数接受两个参数:

  1. string:要解析的字符串。
  2. radix(可选):指定解析的进制,取值范围为 2 到 36。

radix 参数被省略时,parseInt 会尝试根据字符串的前缀来确定基数:

  • 0x0X 开头的字符串被视为 16 进制。
  • 0 开头的字符串可能被视为 8 进制(取决于环境)。
  • 其他情况下,默认基数为 10。

逐步解析 ['1', '5', '11'].map(parseInt)

我们逐一分析 parseInt 的行为:

  1. 对于 '1'(索引 0):

    parseInt('1', 0);

    基数为 0 时,parseInt 会将其视为 10 进制。

    • 结果:1
  2. 对于 '5'(索引 1):

    parseInt('5', 1);

    基数为 1 是无效的,因为基数必须在 236 之间。

    • 结果:NaN
  3. 对于 '11'(索引 2):

    parseInt('11', 2);

    基数为 2,表示按二进制解析字符串。

    • 解析:1 * 2^1 + 1 * 2^0 = 3
    • 结果:3

最终结果

因此,代码返回的结果是:

[1, NaN, 3]

如何避免该问题

为了避免类似问题,我们可以确保 parseInt 函数只接收一个参数,而不使用多余的参数:

解决方案 1:使用箭头函数

显式传递单个参数给 parseInt

['1', '5', '11'].map(num => parseInt(num));
// 结果:[1, 5, 11]

解决方案 2:使用 Number 函数

Number 函数只接受一个参数,并且可以直接将字符串转换为数字:

['1', '5', '11'].map(Number);
// 结果:[1, 5, 11]

解决方案 3:使用一元加号

使用一元加号 + 运算符强制转换字符串为数字:

['1', '5', '11'].map(x => +x);
// 结果:[1, 5, 11]

总结

['1', '5', '11'].map(parseInt) 的行为是由于 map 方法传递了额外的 index 参数给 parseInt,从而导致 parseInt 的第二个参数被解释为基数(radix)。为避免这种意外行为,可以使用显式的箭头函数或其他更合适的方法(如 Number+)来转换字符串为数字。