🔥 面试官:["1", "2", "3"].map(parseInt) 输出什么?90%的前端都答错了!

53 阅读5分钟

🔥 面试官:["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]?

  1. 理解 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 - 原数组本身

  1. parseInt 函数的参数特性

parseInt函数接收两个参数:

· string - 要解析的字符串 · radix - 进制基数(2-36)

  1. 参数错位的陷阱

当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 只接受一个参数,不会出现参数错位

📝 总结与最佳实践

核心要点

  1. 理解函数参数传递:知道每个函数接收的参数个数和含义
  2. 明确进制指定:使用parseInt时总是明确指定第二个参数
  3. 选择合适的转换方法:根据场景选择Number、parseInt或parseFloat
  4. 善用箭头函数:避免不必要的参数传递

面试回答技巧

"这道题考察的是对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

如果觉得这篇文章对你有帮助,请点赞 👍 收藏 ⭐ 支持一下!