腾讯面试题解析:为什么 `['1','2','3'].map(parseInt)` 返回 `[1,NaN,NaN]`?

91 阅读2分钟

腾讯面试题解析:为什么 ['1','2','3'].map(parseInt) 返回 [1,NaN,NaN]

一、问题现象

['1','2','3'].map(parseInt); // 输出:[1, NaN, NaN]

直观上,我们期望输出 [1, 2, 3],但实际结果却是 [1, NaN, NaN]。这与 JavaScript 中 mapparseInt 的协作机制有关。


二、关键原理剖析

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')1radix=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 一致)

六、总结

  • 根本原因mapparseInt 传递了多余的 index 参数,导致基数错误。
  • 最佳实践
    1. 始终明确指定 parseInt 的基数(如 parseInt(str, 10))。
    2. 优先使用 Number+ 进行简单转换。
  • 面试考点
    考察对 JS 函数式编程、参数传递、类型转换的深入理解。

扩展思考:其他类似陷阱
console.log(['10','10','10'].map(parseInt)); // [10, NaN, 2]
第三项 parseInt('10',2) 按二进制解析 → 1*2^1 + 0*2^0 = 2