🔥 面试官:["1", "2", "3"].map(parseInt) 输出什么?90%的前端都答错了!
💡 摘要
一道经典的JavaScript面试题,看似简单却暗藏玄机。本文将深入剖析["1", "2", "3"].map(parseInt)的执行机制,带你彻底理解函数参数传递的陷阱,并提供多种解决方案和最佳实践。
📌 关键词
JavaScript、面试题、parseInt、map、进制转换、函数参数、NaN
🎯 引言
"小张,来说说["1", "2", "3"].map(parseInt)的输出结果吧。"
"这还不简单?肯定是[1, 2, 3]啊!"
"很遗憾,答案是[1, NaN, NaN]。"
这样的对话在前端面试中屡见不鲜。今天,我们就来彻底拆解这个让无数开发者"翻车"的经典问题。
🔍 问题分析:为什么不是 [1, 2, 3]?
- 理解 map 函数的回调机制
// 你以为的调用方式:
["1", "2", "3"].map(parseInt)
// 等价于:
["1", "2", "3"].map(item => parseInt(item))
// 实际上的调用方式:
["1", "2", "3"].map((item, index, array) => parseInt(item, index, array))
关键点:map方法会给回调函数传递三个参数:
· currentValue - 当前处理的元素 · index - 当前元素的索引 · array - 原数组本身
- parseInt 函数的参数特性
parseInt函数接收两个参数:
· string - 要解析的字符串 · radix - 进制基数(2-36)
- 参数错位的陷阱
当parseInt作为map的回调时,实际接收的参数对应关系为:
parseInt(item, index, array)
// 相当于:
parseInt("1", 0, ["1","2","3"]) // 第一次迭代
parseInt("2", 1, ["1","2","3"]) // 第二次迭代
parseInt("3", 2, ["1","2","3"]) // 第三次迭代
🧩 逐行解析执行过程
第一次迭代:parseInt("1", 0)
parseInt("1", 0)
// radix = 0,按特殊规则处理:
// - 字符串以 "0x" 开头 → 16进制
// - 字符串以 "0" 开头 → 8进制或10进制
// - 其他情况 → 10进制
// 此处按10进制解析 → 结果:1 ✅
第二次迭代:parseInt("2", 1)
parseInt("2", 1)
// radix = 1,不在2-36的有效范围内
// 根据规范,直接返回 NaN ❌
第三次迭代:parseInt("3", 2)
parseInt("3", 2)
// radix = 2,按二进制解析
// 但二进制中有效数字只有0和1
// 数字'3'在二进制中无效 → 结果:NaN ❌
最终结果:[1, NaN, NaN]
📚 深入理解 parseInt 的进制规则
正常的进制转换
// ✅ 有效转换
parseInt("1010", 2) // 10 (二进制)
parseInt("77", 8) // 63 (八进制)
parseInt("FF", 16) // 255 (十六进制)
parseInt("42", 10) // 42 (十进制)
导致 NaN 的情况
// ❌ 无效转换
parseInt("3", 2) // NaN (二进制没有3)
parseInt("8", 8) // NaN (八进制没有8)
parseInt("2", 1) // NaN (1进制不存在)
parseInt("G", 16) // NaN (十六进制没有G)
特殊的 radix = 0 情况
// radix = 0 的自动判断规则
parseInt("10", 0) // 10 (按10进制)
parseInt("0x10", 0) // 16 (检测到0x,按16进制)
parseInt("010", 0) // 10 (现代JS中按10进制)
💡 解决方案与最佳实践
方案1:明确指定进制(推荐👍)
["1", "2", "3"].map(item => parseInt(item, 10))
// 结果:[1, 2, 3] ✅
方案2:使用 Number 函数
["1", "2", "3"].map(Number)
// 结果:[1, 2, 3] ✅
// 优点:更严格,整个字符串必须是有效数字
方案3:一元加号运算符
["1", "2", "3"].map(item => +item)
// 结果:[1, 2, 3] ✅
// 优点:简洁高效
方案4:箭头函数包装
["1", "2", "3"].map(item => parseInt(item))
// 结果:[1, 2, 3] ✅
// 优点:明确参数传递
🔄 Number() vs parseInt() 对比
转换规则差异
// Number() - 严格转换
Number("123") // 123
Number("123.45") // 123.45
Number("123abc") // NaN
Number("") // 0
Number(" 123 ") // 123
// parseInt() - 解析转换
parseInt("123") // 123
parseInt("123.45") // 123
parseInt("123abc") // 123
parseInt("") // NaN
parseInt(" 123 ") // 123
使用场景建议
· Number():严格的数字验证,需要精确数值 · parseInt():进制转换,去除单位,容错性要求高的场景 · parseFloat():需要保留小数的解析
🛠️ 实际应用场景
场景1:表单数据处理
const userInputs = ["25", "18", "invalid", "42px"];
// 错误方式 ❌
userInputs.map(parseInt) // [25, NaN, NaN, 4]
// 正确方式 ✅
userInputs.map(item => parseInt(item, 10)) // [25, 18, NaN, 42]
userInputs.map(Number) // [25, 18, NaN, NaN]
// 过滤有效数字
const validNumbers = userInputs
.map(item => parseInt(item, 10))
.filter(item => !isNaN(item)); // [25, 18, 42]
场景2:多种进制转换
// 二进制数组转十进制
const binaryArray = ["1010", "1101", "1001"];
const decimalArray = binaryArray.map(bin => parseInt(bin, 2));
// 结果:[10, 13, 9]
// 十六进制颜色值转换
const hexColors = ["FF", "2A", "10"];
const decimalColors = hexColors.map(hex => parseInt(hex, 16));
// 结果:[255, 42, 16]
场景3:CSS单位提取
const cssValues = ["100px", "2.5rem", "50%", "1.2em"];
// 提取数值
const values = cssValues.map(value => parseInt(value, 10));
// 结果:[100, 2, 50, 1]
// 保留小数
const floatValues = cssValues.map(parseFloat);
// 结果:[100, 2.5, 50, 1.2]
🎓 举一反三:类似的陷阱
setTimeout 的类似问题
[1, 2, 3].map(setTimeout)
// 等价于:
[1, 2, 3].map((item, index) => setTimeout(item, index))
// 第一个参数应该是函数,不是数字
parseFloat 的安全性
["1.1", "2.2", "3.3"].map(parseFloat)
// 结果:[1.1, 2.2, 3.3] ✅
// parseFloat 只接受一个参数,不会出现参数错位
📝 总结与最佳实践
核心要点
- 理解函数参数传递:知道每个函数接收的参数个数和含义
- 明确进制指定:使用parseInt时总是明确指定第二个参数
- 选择合适的转换方法:根据场景选择Number、parseInt或parseFloat
- 善用箭头函数:避免不必要的参数传递
面试回答技巧
"这道题考察的是对map和parseInt参数传递的理解。map传递三个参数,而parseInt的第二个参数是进制基数,索引值作为进制数传递导致异常。正确的做法是使用箭头函数明确参数:arr.map(item => parseInt(item, 10))。"
编码规范建议
· 使用ESLint规则检测潜在的参数错位问题 · 在团队代码规范中明确数字转换的最佳实践 · 对进制转换代码添加必要的注释说明
🎯 思考题
// 1. 下面代码的输出是什么?
["10", "10", "10", "10"].map(parseInt)
// 2. 如何修复这段代码?
const data = ["101", "202", "303"];
const result = data.map(parseInt);
// 3. Number 和 parseInt 在处理 "123abc" 时有何不同?
欢迎在评论区分享你的答案和见解!
📚 参考资料
· MDN - Array.prototype.map() · MDN - parseInt() · ECMAScript 规范 - parseInt
如果觉得这篇文章对你有帮助,请点赞 👍 收藏 ⭐ 支持一下!