为什么 ['1','5','11'].map(parseInt)
会返回 [1, NaN, 3]
问题背景
在 JavaScript 中,使用 Array.prototype.map
方法时,parseInt
函数的行为可能会让人感到困惑。
示例代码:
['1', '5', '11'].map(parseInt);
该代码返回的结果是:
[1, NaN, 3]
以下是对此行为的详细分析。
解析过程
map
方法的执行机制
map
方法会对数组中的每个元素调用回调函数,并传入以下三个参数:
- 当前元素的值 (
element
) - 当前元素的索引 (
index
) - 原数组 (
array
)
调用 map(parseInt)
等价于:
['1', '5', '11'].map((element, index) => parseInt(element, index));
parseInt
的参数
parseInt
函数接受两个参数:
string
:要解析的字符串。radix
(可选):指定解析的进制,取值范围为 2 到 36。
当 radix
参数被省略时,parseInt
会尝试根据字符串的前缀来确定基数:
- 以
0x
或0X
开头的字符串被视为 16 进制。 - 以
0
开头的字符串可能被视为 8 进制(取决于环境)。 - 其他情况下,默认基数为 10。
逐步解析 ['1', '5', '11'].map(parseInt)
我们逐一分析 parseInt
的行为:
-
对于
'1'
(索引0
):parseInt('1', 0);
基数为
0
时,parseInt
会将其视为 10 进制。- 结果:
1
- 结果:
-
对于
'5'
(索引1
):parseInt('5', 1);
基数为
1
是无效的,因为基数必须在2
到36
之间。- 结果:
NaN
- 结果:
-
对于
'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
或 +
)来转换字符串为数字。