我的个人博客

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"]

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

考虑这种情况 - 我们需要获取所有作为数字id

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

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

我们最近在工作中遇到了这个问题,花了我一些时间才弄明白,以下是答案...

通常,我们使用带有 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))
 
/* 索引为 0 */ parseInt('1', 0) // => 1
/* 索引为 1 */ parseInt('2', 1) // => NaN
/* 索引为 2 */ parseInt('3', 2) // => NaN
/* 索引为 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 函数的内容,欢迎在评论区分享你的用例

编码愉快!

参考资料