在 JavaScript 面试中,经常会遇到这样的一道题目:
['1', '2', '3'].map(parseInt); // 输出什么?
你可能会以为它等价于:
['1', '2', '3'].map(num => parseInt(num)); // [1, 2, 3]
但其实它的输出是:
[1, NaN, NaN]
为什么会这样?这背后涉及了 JavaScript 中几个关键的知识点:map 方法的行为、parseInt 函数的参数机制、以及类型转换中的 NaN。
一、先看 map 方法的基本行为
Array.prototype.map 是 ES5 就引入的方法,用于对数组中的每个元素进行处理并返回一个新数组。
它的基本语法如下:
arr.map(callback(currentValue[, index[, array]])[, thisArg])
-
callback是一个函数,接收三个参数:currentValue:当前元素值index(可选):当前元素索引array(可选):原数组本身
我们可以通过以下方式观察 map 的执行过程:
['1','2','3'].map((num, index, arr) => {
console.log('当前元素:', num);
console.log('当前索引:', index);
console.log('原数组:', arr);
return num;
});
这段代码会打印出每个元素、索引和原数组:
当前元素: 1
当前索引: 0
原数组: ['1', '2', '3']
当前元素: 2
当前索引: 1
原数组: ['1', '2', '3']
当前元素: 3
当前索引: 2
原数组: ['1', '2', '3']
因此,在调用 ['1', '2', '3'].map(parseInt) 时,实际上是将每个元素作为第一个参数传给 parseInt,而第二个参数是索引(index) 。
二、再来看 parseInt 函数的参数机制
parseInt(string, radix) 接收两个参数:
string:要解析的字符串radix(可选):进制数(2 到 36 之间)
如果省略 radix 或其值为 0,JavaScript 会根据字符串前缀来决定进制:
"0x"或"0X"开头,按十六进制解析- 其他情况,默认按十进制解析
但如果提供了 radix,则严格按照该进制解析。
三、结合两者分析:为什么是 [1, NaN, NaN]?
我们来手动模拟一下这个过程:
第一次循环:索引 0
parseInt('1', 0)
radix = 0,此时 JS 会自动判断进制。'1'不是以'0x'开头,所以默认按十进制解析。- 结果:
1
✅ 正确
第二次循环:索引 1
parseInt('2', 1)
radix = 1,这是非法的进制数(合法范围是 2~36)- 因此
parseInt忽略第二个参数,只解析第一个参数'2' - 但因为
radix不合法,整个结果返回NaN
❌ 错误,结果为 NaN
第三次循环:索引 2
parseInt('3', 2)
- 进制是 2(二进制),尝试将
'3'解析为二进制数字 - 但在二进制中,只能有
0和1,3是无效字符 - 所以返回
NaN
❌ 错误,结果为 NaN
📌 什么是 NaN
在 JavaScript 中,
NaN表示“不是数字”(Not-a-Number),但它有一个奇怪的特点:
typeof NaN; // "number"
也就是说,虽然它是“不是数字”,但它的类型却是
"number"。 判断是否为NaN的正确方法是使用:
isNaN(value); // 不推荐,容易出错
Number.isNaN(value); // 推荐,ES6 新增
四、如何正确使用 map + parseInt?
如果你想把字符串数组转成数字数组,应该写成:
['1', '2', '3'].map(Number); // [1, 2, 3]
或者:
['1', '2', '3'].map(str => parseInt(str)); // [1, 2, 3]
如果你一定要用 parseInt 并确保进制是 10:
['1', '2', '3'].map(str => parseInt(str, 10)); // [1, 2, 3]
结语
这道看似简单的题目,实际上考察了多个 JS 基础知识,包括:
- 高阶函数的理解(如
map) - 类型转换(字符串 → 数字)
- 参数传递机制(
parseInt的第二个参数) - 对
NaN的理解
对于前端开发者来说,掌握这些细节不仅有助于通过面试,也能写出更健壮、可靠的代码。