Яка різниця між Map і foreach в Array
map повертає новий масив з трансформованими значеннями; forEach запускає функцію на кожному елементі і повертає undefined.
Теорія
TL;DR
mapяк ксерокс з фільтром: отримуєш новий стос аркушів, оригінал не змінивсяforEachяк маркер: позначає щось на місці і нічого не повертає- Головна різниця:
mapповертає новий масив,forEachповертаєundefined - Потрібен новий масив?
map. Просто логування або зовнішні зміни?forEach - Після
forEachне можна будувати ланцюжок методів; післяmapможна:.filter(),.reduce()
Швидкий приклад
const nums = [1, 2, 3];
// map повертає новий масив
const doubled = nums.map(n => n * 2);
console.log(doubled); // [2, 4, 6]
console.log(nums); // [1, 2, 3] - без змін
// forEach повертає undefined
const result = nums.forEach(n => n * 2);
console.log(result); // undefinedОбидва методи обходять кожен елемент. map збирає повернені значення колбеку в новий масив. forEach їх ігнорує.
Ключова різниця
map будує і повертає новий масив тієї ж довжини, де кожна позиція містить те, що повернув колбек. Оригінальний масив залишається незмінним. forEach запускає колбек суто для побічних ефектів, ігнорує повернене значення і завжди повертає undefined. Ця одна різниця визначає все. Плутанина між цими двома методами одна з найчастіших речей у код-рев'ю з джунами.
Коли що використовувати
- Трансформувати дані для відображення (форматувати імена, конвертувати одиниці, змінити структуру об'єктів) →
map - Логувати або дебажити вміст масиву →
forEach - Будувати ланцюжок
.map().filter().reduce()→map - Додавати елементи в зовнішній масив або робити API-виклики в циклі →
forEach - Рендерити JSX-списки в React →
map(forEachповертаєundefinedі зламає рендер)
Порівняльна таблиця
| Аспект | map | forEach |
|---|---|---|
| Повертає | Новий масив | undefined |
| Оригінальний масив | Не змінюється | Не змінюється (якщо колбек не мутує) |
| Ланцюжок методів | Так | Ні |
| Використовують для | Трансформацій | Побічних ефектів |
| Швидкість | Трохи повільніше (виділяє пам'ять) | Трохи швидше (без виділення пам'яті) |
Як це працює всередині
Обидва методи перебирають елементи масиву і викликають колбек з аргументами (value, index, array). Різниця в тому, що відбувається з поверненим значенням. map заздалегідь виділяє новий масив тієї ж довжини і записує кожен результат на відповідну позицію. forEach викидає кожне повернене значення і виходить з undefined.
Типові помилки
Очікувати від forEach повернення масиву:
const doubled = [1, 2, 3].forEach(n => n * 2);
console.log(doubled); // undefined, а не [2, 4, 6]Рішення: використовуй map.
Ланцюжок після forEach:
[1, 2, 3].forEach(x => x * 2).filter(x => x > 2);
// TypeError: Cannot read properties of undefinedРішення: спочатку map, потім ланцюжок.
Мутація оригіналу всередині map:
const arr = [1, 2, 3];
arr.map((n, i) => { arr[i] = n * 2; }); // мутує arr напрямуmap не захищає від мутацій. Якщо мутуєш всередині нього, це не той інструмент. map призначений для повернення нових значень, не для зміни оригіналу.
Використання map суто для побічних ефектів:
users.map(user => console.log(user.name)); // виділяє масив, якого ніхто не використовуєРішення: users.forEach(user => console.log(user.name)).
Де зустрічається в реальних проектах
- React:
users.map(user => <UserCard key={user.id} user={user} />)- рендер списків - Redux:
state.items.map(item => ({ ...item, isNew: true }))- іммутабельне оновлення стану - Express:
req.body.users.map(user => user.id)- витягнути ID перед запитом до БД - Node.js:
data.forEach(chunk => stream.write(chunk))- запис без збору результатів
Питання на співбесіді
Q: Що повертає forEach?
A: Завжди undefined. Повернене значення колбеку повністю ігнорується.
Q: Чи можна мутувати оригінальний масив всередині map?
A: Технічно так, але не варто. map призначений для повернення нових значень. Якщо потрібна мутація, використовуй звичайний цикл for.
Q: Коли forEach швидший за map?
A: Завжди трохи швидший, бо не виділяє пам'ять під новий масив. Різниця помітна лише на дуже великих масивах.
Q: Чому map працює в рендері React-компонента, а forEach ні?
A: React очікує масив JSX-елементів. map повертає цей масив. forEach повертає undefined, і React не знає що з ним робити.
Q: Реалізуй map через forEach.
A: const myMap = (arr, fn) => { const result = []; arr.forEach((item, i) => { result[i] = fn(item, i, arr); }); return result; };
Приклади
Трансформація даних з API
Типовий сценарій: отримуємо сирі об'єкти з API і потрібна інша структура перед передачею в компонент.
const users = [
{ id: 1, firstName: 'Anna', lastName: 'Smith' },
{ id: 2, firstName: 'Bob', lastName: 'Jones' },
];
// map будує новий масив з потрібною структурою
const displayNames = users.map(user => ({
id: user.id,
fullName: `${user.firstName} ${user.lastName}`,
}));
console.log(displayNames);
// [{ id: 1, fullName: 'Anna Smith' }, { id: 2, fullName: 'Bob Jones' }]
console.log(users); // оригінал не змінивсяmap тут правильний вибір, бо потрібен новий масив для компонента. forEach змусив би оголосити зовнішній масив і робити push вручну: більше коду, той самий результат.
Побічні ефекти з forEach
const orders = [
{ id: 101, total: 49.99, status: 'pending' },
{ id: 102, total: 120.0, status: 'shipped' },
];
// forEach: виводимо кожне замовлення, новий масив не потрібен
orders.forEach(order => {
console.log(`Order #${order.id}: $${order.total} (${order.status})`);
});
// Умовний побічний ефект для кожного елемента
orders.forEach(order => {
if (order.status === 'pending') {
sendReminderEmail(order.id);
}
});Повернене значення тут не потрібне. map теж спрацює, але виділить масив із undefined, якого ніхто не використовує.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.