腾讯面试题解析:为什么 ['1','2','3'].map(parseInt) 返回 [1,NaN,NaN]?
一、问题现象
['1','2','3'].map(parseInt); // 输出:[1, NaN, NaN]
直观上,我们期望输出 [1, 2, 3],但实际结果却是 [1, NaN, NaN]。这与 JavaScript 中 map 和 parseInt 的协作机制有关。
二、关键原理剖析
1. map 的底层机制
- 参数传递规则:
map的回调函数接收三个参数:
(currentValue, index, array)
例如:
['1','2','3'].map(parseInt)实际执行的是:parseInt('1', 0); // 第一次调用 parseInt('2', 1); // 第二次调用 parseInt('3', 2); // 第三次调用
2. parseInt 的陷阱
-
函数定义:
parseInt(string, radix)string:要解析的字符串(自动截断非数字后缀)。radix:基数(进制),范围是 2~36。若为0或未提供,则按十进制处理(但某些浏览器可能将0视为未提供)。
-
关键规则:
- 若
radix < 2或> 36→ 返回NaN。 - 若待解析字符超出当前进制的有效范围 → 返回
NaN。
- 若
三、逐行解析结果
| 迭代 | 实际调用 | 等效代码 | 结果 | 原因 |
|---|---|---|---|---|
| 第一次 | parseInt('1', 0) | parseInt('1') | 1 | radix=0 被当作十进制处理 |
| 第二次 | parseInt('2', 1) | - | NaN | 基数 1 不在 2~36 范围内 |
| 第三次 | parseInt('3', 2) | - | NaN | 二进制下有效数字是 0~1,'3' 是非法字符 |
四、解决方案
方法 1:显式指定 parseInt 的基数
['1','2','3'].map(str => parseInt(str, 10)); // [1, 2, 3]
方法 2:使用 Number 代替 parseInt
['1','2','3'].map(Number); // [1, 2, 3]
方法 3:一元运算符 +
['1','2','3'].map(str => +str); // [1, 2, 3]
五、map 的底层实现(简化版)
理解 map 的源码级逻辑能避免此类问题:
Array.prototype.myMap = function(callback) {
const result = [];
for (let i = 0; i < this.length; i++) {
// 关键点:回调接收三个参数 (currentValue, index, array)
result.push(callback(this[i], i, this));
}
return result;
};
// 测试错误用法
['1','2','3'].myMap(parseInt); // 输出 [1, NaN, NaN](与原生 map 一致)
六、总结
- 根本原因:
map向parseInt传递了多余的index参数,导致基数错误。 - 最佳实践:
- 始终明确指定
parseInt的基数(如parseInt(str, 10))。 - 优先使用
Number或+进行简单转换。
- 始终明确指定
- 面试考点:
考察对 JS 函数式编程、参数传递、类型转换的深入理解。
扩展思考:其他类似陷阱
console.log(['10','10','10'].map(parseInt)); // [10, NaN, 2]
第三项parseInt('10',2)按二进制解析 →1*2^1 + 0*2^0 = 2。