【译】map & forEach 的区别

2,920 阅读2分钟

原文:What Is the Difference Between map() and forEach() in JavaScript?

map & forEach 都是从 es5 才开始出现,本文会讨论它们之间主要的区别以及如何使用它们。

简介

map & forEach 都是用来更方便地遍历数组的。

map 接收两个参数:callback 函数,它会在 map 执行之后被触发。上下文变量,即执行 callback 函数时 this 指向的对象。map 会返回一个新数组。

map(callback[, thisArg])

[1, 2, 3].map(function(value, index, originalArray) {
  console.log(`${index}: ${value} / ${originalArray} /`);
  console.log(this);
  return value + 1;
}, { test: 1 });
// 0: 1 / 1,2,3 / {test: 1}
// 1: 2 / 1,2,3 / {test: 1}
// 2: 3 / 1,2,3 / {test: 1}
// 返回值:[2, 3, 4]

注意:map 的返回不等于原数组:

const arr = [1];
const new_arr = arr.map(d => d);
arr === new_arr; // false

forEach 接收的参数和 map 相同,但是它没有返回值,即它返回的是 undefined。

forEach(callback[, thisArg])

[1, 2, 3].forEach(function(value, index, originalArray) {
  console.log(`${index}: ${value} / ${originalArray} /`);
  console.log(this);
}, { test: 1 });
// 0: 1 / 1,2,3 / {test: 1}
// 1: 2 / 1,2,3 / {test: 1}
// 2: 3 / 1,2,3 / {test: 1}
// 返回值:undefined

使用场景

因为 map & forEach 的主要区别是有无返回,所以,当你想基于一个原数组返回一个新数组,可以选择 map,当你只是想遍历数据不需要考虑返回时可以选择 forEach。

map

const people = [
  { name: 'Josh', whatCanDo: 'painting' },
  { name: 'Lay', whatCanDo: 'security' },
  { name: 'Ralph', whatCanDo: 'cleaning' }
];

function makeWorkers(people) {
  return people.map((person) => {
    const { name, whatCanDo } = person;
    return <li key={name}>My name is {name}, I can do {whatCanDo}</li>
  });
}

<ul>makeWorkers(people)</ul>

注意:map 遍历数组时,每次都会返回一个值,如果没有显示返回,就返回 undefined,如 e.g.1。

const metrics = [
  { id: 'sales', selected: true, title: 'Sales'}, 
  { id: 'units', selected: true, title: 'Units'}, 
  { id: 'buyers', selected: false, title: 'Buyers'}
]
e.g.1
const ids = metrics.map(item => {
  if(item.selected) {
    return item.id
  }
})
//  ["sales", "units", undefined]

在实际开发中我碰到这样一个逻辑:返回 metrics 中 selected:true 的元素的 id 组成的数组,因为想用一个函数搞定,然后就写了 e.g.2 这段代码,然鹅。。。可以选择用 e.g.3 / e.g.4 / e.g.5。

e.g.2
const ids = metrics.map(item => item.selected && item.id)
//  ["sales", "units", false]
e.g.3
const ids = metrics.filter(item => item.selected).map(item => item.id)
e.g.4
const ids = []
metrics.forEach(item => {
  item.selected && ids.push(item.id)
}
e.g.5
const ids = metrics.reduce((newArr, item) => {
  item.selected && newArr.push(item.id)
  return newArr;
}, []);

速度对比

一些文章说 map 比 forEach 快,为了验证,我找了以下对比:

img

img

代码看起来差不多,但是结果却截然相反。一些 test cases 显示 forEach 快,一些显示 map 快。老实说,我也不确定,但是我觉的在现代 web 开发中不必纠结这点速度,代码的可读性更重要。

但有一点很确定,他们都比 for 循环慢。

结论

map & forEach 都是遍历可迭代对象的利器,可以简化代码,增加代码的可读性。我们只需区分使用它们的场景就行。