JavaScript 有一些方便的方法可以帮助我们遍历数组。最常用于迭代的两个是 Array.prototype.map() 和 Array.prototype.forEach()

但我认为它们仍然有点不清楚,特别是对于初学者来说。因为它们都进行了迭代并输出了一些东西。那么区别是什么呢?

在本文中,我们将研究以下内容:

  • 定义
  • 返回值
  • 是否能够链接其他方法
  • 可变性
  • 性能速度
  • 小结

定义

map 方法接收一个函数作为参数。然后它将参数应用于每个元素并返回一个全新的数组,其中填充了调用提供的函数的结果。

这意味着它返回一个新数组,其中包含数组每个元素的执行结果。它将始终返回相同数量的项目。

const myAwesomeArray = [5, 4, 3, 2, 1]
myAwesomeArray.map(x => x * x)
 
// >>>>>>>>>>>>>>>>> Output: [25, 16, 9, 4, 1]

与 map 一样,forEach() 方法接收一个函数作为参数,并对每个数组元素执行一次。但是,它不会返回像 map 这样的新数组,而是返回 undefined

const myAwesomeArray = [
  { id: 1, name: "john" },
  { id: 2, name: "Ali" },
  { id: 3, name: "Mass" },
]
 
myAwesomeArray.forEach(element => console.log(element.name))
// >>>>>>>>> Output : john
//                    Ali
//                    Mass

返回值

map() 和 forEach() 之间的第一个区别是返回值。forEach() 方法返回 undefined,而 map() 返回一个包含转换后元素的新数组。即使它们做同样的工作,返回值却不同。

const myAwesomeArray = [1, 2, 3, 4, 5]
myAwesomeArray.forEach(x => x * x)
//>>>>>>>>>>>>>return value: undefined
 
myAwesomeArray.map(x => x * x)
//>>>>>>>>>>>>>return value: [1, 4, 9, 16, 25]

是否能够链接其他方法

这些数组方法之间的第二个区别是 map() 是可链接的。这意味着你可以在对数组执行 map() 方法后附加 reduce()sort()filter() 等。

这是你不能用 forEach() 做的事情,因为你可能猜到,它返回 undefined

const myAwesomeArray = [1, 2, 3, 4, 5]
myAwesomeArray.forEach(x => x * x).reduce((total, value) => total + value)
//>>>>>>>>>>>>> Uncaught TypeError: Cannot read property 'reduce' of undefined
myAwesomeArray.map(x => x * x).reduce((total, value) => total + value)
//>>>>>>>>>>>>>return value: 55

可变性

一般来说,“mutate” 这个词意味着改变、交替、修改或变换。在 JavaScript 世界中,它具有相同的含义。

可变对象是在创建后可以修改其状态的对象。那么,forEach 和 map 的可变性呢?

根据 MDN 文档

forEach() 不会改变调用它的数组。(但是,callback 可能会这样做)。

map() 不会改变调用它的数组(尽管 callback 可能会这样做)。

JavaScript 很奇怪。

在这里,我们看到了一个非常相似的定义,我们都知道它们都接收 callback 作为参数。那么,哪一个依赖于不变性?

在我看来,这个定义并不清楚。要知道哪个不会改变原始数组,我们首先要检查这两种方法是如何工作的。

map() 方法返回一个全新的数组,其中包含转换后的元素和相同数量的数据。对于 forEach() 的情况下,即使它返回 undefined,它也会用 callback 改变原始数组。

因此,我们清楚地看到 map() 依赖于不变性,而 forEach() 是一个 mutator 方法。

注意❗

如果数组是引用类型,map是可以改变原数组的。

性能速度

关于性能速度,它们有点不同。但是,有关系吗?嗯,这取决于各种因素,例如你的计算机、你正在处理的数据量等等。

你可以使用下面的示例或使用 jsPerf 自行检查,看看哪个更快。

const myAwesomeArray = [1, 2, 3, 4, 5]
 
const startForEach = performance.now()
myAwesomeArray.forEach(x => (x + x) * 10000000000)
const endForEach = performance.now()
console.log(`Speed [forEach]: ${endForEach - startForEach} miliseconds`)
 
const startMap = performance.now()
myAwesomeArray.map(x => (x + x) * 10000000000)
const endMap = performance.now()
console.log(`Speed [map]: ${endMap - startMap} miliseconds`)

小结

map() 和 forEach()之间的选择将取决于你的用例。如果你计划更改、替换或使用数据,那么你应该选择 map(),因为它会返回一个包含转换后数据的新数组。

但是,如果你不需要返回的数组,请不要使用 map() - 而是使用 forEach() 甚至 for 循环。

扩展阅读