JavaScript 中 Array.prototype.map 的棘手用例

Boy playing jenga
Published on
/5 mins read/---

如果你熟悉函数式编程Array.prototype.map 一定是你每天都在使用的函数

对我来说,map() 是一个功能强大的函数,但它可能有一些你不知道的棘手用例!

让我们先了解一些基础知识

map() 方法通过对调用数组的每个元素应用提供的回调函数一次,从调用数组创建一个新数组

简单用例

给定这个数组

let devs = [
  { id: '1', name: 'Leo', yob: 1995 },
  { id: '2', name: 'Paul', yob: 1995 },
  { id: '3', name: 'Jesse', yob: 1996 },
  { id: '4', name: 'Ken', yob: 1997 },
]

我们可以使用 map() 函数做什么:

  • 从数组中获取新值
let ages = devs.map((dev) => {
  let currentYear = new Date().getFullYear()
  return currentYear - dev.yob
})
 
console.log(ages) // => [25, 25, 24, 23]
  • 从对象数组中提取值
let names = devs.map((dev) => dev.name)
 
console.log(names) // => ["Leo", "Paul", "Jesse", "Ken"]
  • 在 React 应用程序中渲染列表
import React from 'react'
 
export default DeveloperProfiles = ({ devs }) => {
  return (
    <ul>
      {devs.map((dev) => (
        <li key={dev.id}>{dev.name}</li>
      ))}
    </ul>
  )
}

棘手的用例

通常将预定义的回调函数作为 map() 参数传递是很常见的,就像这样:

let extractId = (dev) => {
  return dev.id
}
 
let ids = devs.map(extractId)
 
console.log(ids) // => ["1", "2", "3", "4"]

但这种习惯可能会导致函数接受额外参数时出现意外行为。

考虑这种情况 - 我们需要将所有 ids 转换为数字

// ids = ["1", "2", "3", "4"]
let idsInNumber = ids.map(parseInt)
 
console.log(idsInNumber) // => ???

你可能期望结果是 [1, 2, 3, 4],但实际结果是 [1, NaN, NaN, NaN] 😲

我们最近在 Cốc Cốc 遇到了这个问题,花了我一段时间才弄明白,这里是答案...

通常,我们使用带有 1 个参数(正在遍历的元素)的 map() 回调,但 Array.prototype.map 传递了 3 个参数:

  • 元素
  • 索引
  • 调用数组(大多数时候我们不使用)

而这种情况下的回调 - parseInt 通常与 1 个参数一起使用,但它接受 2 个:

// 语法
parseInt(string [, radix])
  • string: 要解析的值
  • radix: [可选]: 表示基数(数学数字系统中的底数)的整数 - 通常是 10

例如:

parseInt('10') // => 10
parseInt('10', 10) // => 10
parseInt('10', 2) // => 2
parseInt('10', 4) // => 4

在这里你可以看到混乱的根源 👀!

map() 的第三个参数parseInt 忽略 - 但第二个参数不是

因此,map()迭代步骤如下所示:

// map(parseInt) => map(parseInt(value, index))
 
/* index is 0 */ parseInt('1', 0) // => 1
/* index is 1 */ parseInt('2', 1) // => NaN
/* index is 2 */ parseInt('3', 2) // => NaN
/* index is 3 */ parseInt('4', 3) // => NaN

解决方案

既然你已经知道了问题的根源,解决方案就是如果你不确定它是如何工作的,就不要将 map() 的所有参数传递给你的回调

  • 只传递你的回调所需的参数
function returnInt(element) {
  return parseInt(element, 10)
}
 
;['1', '2', '3', '4'].map(returnInt)
// => [1, 2, 3, 4]
 
// 与上面相同,但使用简洁的箭头函数语法
// 如果你不需要重复使用回调,最好使用这个
;['1', '2', '3', '4'].map((numb) => parseInt(numb, 10))
// => [1, 2, 3, 4]
;['1', '2', '3', '4'].map(Number)
// => [1, 2, 3, 4]

这就是我对 Array.prototype.map 函数的了解,欢迎在评论区分享你的用例 👇

编程快乐!

参考资料