В чем разница между map() и forEach() в JavaScript?

внешний интерфейс JavaScript
В чем разница между map() и forEach() в JavaScript?

источник:medium.com/better-pro Страна…
Автор: Луна
Перевод: Официальный аккаунт "Full-Stack Front-End Developer"

Одни из самых популярных функций в JavaScript — это, вероятно, map и forEach. Они существуют со времен ECMAScript 5 (сокращенно es5).

В этой статье я расскажу об основных различиях между ними и покажу вам несколько примеров их использования.

Перед чтением

По сути, итерация объекта в JavaScript зависит от того, является ли объект итерируемым или нет. По умолчанию массивы являются итерируемыми.mapиforEachвключен вArray.prototype, поэтому нам не нужно учитывать итерируемость. Если вы хотите узнать больше, я рекомендую вам взглянуть на то, что такое итерации в JavaScript!

Что такое map() и forEach()?

mapиforEachэто вспомогательный метод в массиве, чтобы легко перебирать массив. Мы использовали цикл по массиву, как показано ниже, без каких-либо вспомогательных функций.

var array = ['1', '2', '3'];
for (var i = 0; i < array.length; i += 1) {
  console.log(Number(array[i]));
}
// 1
// 2
// 3

Он существует с начала эры JavaScript.forцикл. Он содержит 3 выражения: начальное значение, условие и конечное выражение.

Это классический способ перебора массива. Начиная с ECMAScript 5 новые функции, кажется, делают нас счастливее.

map

mapроль иforШлейф точно такой же, толькоmapСоздается новый массив, что приводит к вызову предоставленной функции для каждого элемента в вызывающем массиве.

Он принимает два параметра: один вызывается позжеmapилиforEachФункция обратного вызова вызывается, когда вызывается другая функция, когда вызывается функция обратного вызоваthisArgпеременные контекста.

const arr = ['1', '2', '3'];
// 回调函数接受3个参数
// 数组的当前值作为第一个参数
// 当前值在数组中的位置作为第二个参数
// 原始源数组作为第三个参数
const cb = (str, i, origin) => {
  console.log(`${i}: ${Number(str)} / ${origin}`);
};
arr.map(cb);
// 0: 1 / 1,2,3
// 1: 2 / 1,2,3
// 2: 3 / 1,2,3

Функцию обратного вызова можно использовать следующим образом.

arr.map((str) => { console.log(Number(str)); })

mapРезультат не равен исходному массиву.

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

arr === new_arr; // false

Вы также можете использовать объект какthisArgперешли на карту.

const obj = { name: 'Jane' };

[1].map(function() {
  // { name: 'Jane' }
  console.dir(this);
}, obj);

[1].map(() => {
  // window
  console.dir(this);
}, obj);

объектobjстатьmapизthisArg. Но функция обратного вызова стрелки не можетobjкак егоthisArg.

Это связано с тем, что стрелочные функции отличаются от обычных функций.

forEach

forEachэто еще одна функция цикла для массивов, ноmapиforEachразные в использовании.mapиforEachМожно использовать два параметра - функцию обратного вызова иthisArg, которые используются в качестве ихthis.

const arr = ['1', '2', '3'];
// 回调函数接受3个参数
// 数组的当前值作为第一个参数
// 当前值在数组中的位置作为第二个参数
// 原始源数组作为第三个参数
const cb = (str, i, origin) => {
  console.log(`${i}: ${Number(str)} / ${origin}`);
};
arr.forEach(cb);
// 0: 1 / 1,2,3
// 1: 2 / 1,2,3
// 2: 3 / 1,2,3

Какая разница?

mapвозвращает новый массив исходного массива, ноforEachНо это не так. Но оба они обеспечивают неизменность исходного объекта.

[1,2,3].map(d => d + 1); // [2, 3, 4];
[1,2,3].forEach(d => d + 1); // undefined;

Если вы измените значение внутри массива,forEachНеизменяемость массивов не гарантируется. Этот метод гарантирует неизменяемость только в том случае, если вы не трогаете ни одно из значений внутри.

[{a: 1, b: 2}, {a: 10, b: 20}].forEach((obj) => obj.a += 1);
// [{a: 2, b: 2}, {a: 11, b: 21}]
// 数组已更改!

Когда использовать map() и forEach()?

Поскольку основное различие между ними заключается в том, есть ли возвращаемое значение, вы захотите использоватьmapчтобы создать новый массив и использоватьforEachПросто для отображения на массив.

Вот простой пример.

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это очень распространенный способ изготовления элементов, потому чтоmapПосле обработки данных исходного массива создается и возвращается новый массив.

const mySubjectId = ['154', '773', '245'];

function countSubjects(subjects) {
  let cnt = 0;
  
  subjects.forEach(subject => {
    if (mySubjectId.includes(subject.id)) {
      cnt += 1;
    }
  });
  
  return cnt;
}

countSubjects([
  { id: '223', teacher: 'Mark' },
  { id: '154', teacher: 'Linda' }
]);
// 1

С другой стороны, если вы хотите что-то сделать с данными, не создавая новый массив,forEachОчень полезно. Кстати, вы можете использоватьfilterПример с рефакторингом.

subjects.filter(subject => mySubjectId.includes(subject.id)).length;

Подводя итог, я бы рекомендовал вам использовать карту при создании нового массива и forEach, когда вам не нужно создавать новый массив, а что-то делать с данными.

сравнение скорости

В некоторых постах упоминаетсяmapСравниватьforEachбыстрый. Итак, мне любопытно, правда ли это. Я нашел это сравнение.

Код выглядит очень похоже, но результат противоположный. некоторые тесты говорятforEachбыстрее, говорят некоторыеmapБыстрее. Может быть, ты говоришь себеmap/forEachБыстрее других, возможно, вы правы. Честно говоря, я не уверен. Я думаю, что в современной веб-разработке удобочитаемость важнее, чемmapиforEachСкорость между ними гораздо важнее.

Но одно можно сказать точно — оба лучше, чем встроенный JavaScript.forЦиклы медленные.