JavaScript: foreach и map
различия методов forEach и map, о которых должен знать каждый разработчик
Для перебора элементов массива в JavaScript есть несколько удобных методов. Из них наиболее популярны Array.prototype.map() и Array.prototype.forEach(). Для начинающих программистов в использовании этих методов есть несколько неочевидных моментов: ведь, оба метода предназначены для перебора элементов и оба возвращают какой-то результат. Так в чем же различия?
Содержание
Определения
Метод map получает функцию в качестве параметра, применяя ее к каждому элементу массива и возвращая совершенно новый массив, заполненный результатами вызова вышеупомянутой функции.
То есть, этот метод возвращает новый массив, содержащий образы всех элементов массива.
const myAwesomeArray = [5, 4, 3, 2, 1]
myAwesomeArray.map(x => x * x)
// Результат: [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))
// Результат:
// John
// Ali
// Mass
Возвращаемые значения
Возвращаемое значение - основное различие между map и forEach: forEach возвращает undefined, в то время как map возвращает новый массив с измененными элементами. Даже, если оба метода выполняют одну и ту же работу, возвращаемые значения будут разными.
const myAwesomeArray = [1, 2, 3, 4, 5]
myAwesomeArray.forEach(x => x * x)
// Возвращаемое значение: undefined
myAwesomeArray.map(x => x * x)
// Возвращаемое значение: [1, 4, 9, 16, 25]
Связывание методов
Другим важным различием этих методов является то, что map можно связывать с другими методами. Это означает, что Вы можете присоединить методы reduce(), sort(), filter() после применения к массиву метода map().
Вы не сможете проделать то же самое с 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)
// Возвращает значение: 55
Изменяемость
Изменяемый (мутирующий) объект - объект, состояние которого модифицируется после его создания. Попробуем разобраться: какое отношение к изменяемости имеют методы map и forEach.
Если опираться на информацию из документации MDN, оба метода не изменяют массивы (хотя, при вызове callback-функции это происходит).
Мы знаем, что и map, и forEach получают callback-функцию в качестве аргумента. Так, какой же из методов не приводит к изменению массива?
Вспомним, как работает каждый из методов. map возвращает совершенно новый измененный массив, в то время как forEach, возвращая undefined, при определенных условиях изменяет исходный массив с помощью callback-функции.
Именно поэтому map не ведет к мутированию исходного массива, в то время как forEach делает это.
Производительность
Измерение производительности показывает, что различия между map и forEach минимальны и зависят от множества факторов, в том числе от конфигурации компьютера, количества элементов и так далее.
Вы можете самостоятельно проверить производительность с помощью 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.
В случае, если нужно модифицировать элементы исходного массива, подойдет forEach.
Спасибо за внимание.
