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
.
Спасибо за внимание.