Обзор метода forEach для работы с массивами в JavaScript
Основы метода forEach
Метод forEach выполняет переданную функцию один раз для каждого элемента массива. Это наиболее простой способ выполнить побочное действие для каждого элемента без создания нового массива.
const numbers = [1, 2, 3, 4, 5];
numbers.forEach(function(num) {
console.log(num * 2);
});Js filter (метод filter в javascript)
2 4 6 8 10
Js foreach (метод foreach в javascript)
В примере функция принимает текущий элемент и выводит его удвоенное значение. Массив numbers остаётся неизменным. Метод не возвращает новый массив, а только вызывает функцию для каждого элемента.
Как передать в forEach дополнительный контекст (thisArg)?
Вторым аргументом forEach может принимать значение thisArg, которое будет использоваться как this внутри callback-функции.
const numbers = [1, 2, 3];
const multiplier = {
factor: 10,
multiply: function(num) {
return num * this.factor;
}
};
const results = [];
numbers.forEach(function(num) {
results.push(this.multiply(num));
}, multiplier);
console.log(results);
Javascript found (метод found в javascript (возможно, find))
[10, 20, 30]
Js reduce (метод reduce для массивов)
Если callback - стрелочная функция, thisArg игнорируется, так как стрелки не имеют собственного this.
Как получить индекс элемента при обходе?
Callback-функция может принимать второй параметр - индекс текущего элемента.
const fruits = ['яблоко', 'банан', 'вишня'];
fruits.forEach(function(fruit, index) {
console.log(`${index}: ${fruit}`);
});
0: яблоко 1: банан 2: вишня
Третий параметр - сам массив, но он редко используется. Индекс полезен для вывода позиции или изменения элемента по индексу (хотя для мутации лучше использовать другие методы).
Можно ли выйти из forEach досрочно (как break)?
Нет, forEach не поддерживает прерывание через break или continue. Если требуется остановить обход, используют for...of или some/every. Имитировать break можно через выброс исключения, но это излишне.
// Пример с исключением (не рекомендуется)
const arr = [1, 2, 3, 4];
try {
arr.forEach(num => {
if (num === 3) throw new Error('stop');
console.log(num);
});
} catch (e) {
// ничего не делаем
}
1 2
Проблема: Использование исключений для управления потоком усложняет код и снижает производительность. Лучше выбрать другой метод обхода, если требуется прерывание.
Как обойти объект (не массив) с помощью forEach?
Напрямую forEach неприменим к объектам, так как они не являются итерабельными (если только не реализуют Symbol.iterator). Для ключей объекта используют Object.keys() или Object.entries().
const obj = { a: 1, b: 2, c: 3 };
Object.keys(obj).forEach(key => {
console.log(`${key}: ${obj[key]}`);
});
a: 1 b: 2 c: 3
Также можно Object.entries(obj).forEach(...), получая сразу пары [ключ, значение].
Что произойдёт, если изменить массив во время forEach?
forEach обрабатывает элементы, существовавшие на момент вызова. Добавление новых элементов за пределами текущего индекса может привести к пропуску или повторной обработке.
const arr = [1, 2, 3];
arr.forEach((num, index) => {
console.log(num);
if (index === 0) arr.push(4); // добавление в конец
});
1 2 3
Элемент 4 не будет обработан, так как длина массива кэшируется до начала итерации. Удаление текущего или следующего элемента может вызвать сдвиг и пропуски.
Ошибка: Мутация массива во время обхода часто приводит к неожиданному поведению. Рекомендуется сначала собрать изменения в отдельном массиве или использовать filter/map.
Почему forEach не работает с асинхронными функциями как ожидалось?
forEach не ожидает завершения асинхронных операций. Callback, объявленный как async, запускает промисы, но forEach продолжает итерацию не дожидаясь их выполнения.
const urls = ['/api/1', '/api/2'];
urls.forEach(async (url) => {
const response = await fetch(url);
console.log(url, response.status);
});
console.log('Все запросы запущены, но не завершены');
Все запросы запущены, но не завершены (через некоторое время вывод статусов)
Проблема: forEach не возвращает промис, поэтому нельзя дождаться выполнения всех асинхронных задач. Используйте for...of с await или Promise.all с map.
Расширенные примеры использования forEach
1. Подсчёт количества элементов по условию
const scores = [45, 78, 92, 34, 88, 61];
let passed = 0;
scores.forEach(score => {
if (score >= 60) passed++;
});
console.log(`Сдано: ${passed}`);
Сдано: 4
Переменная passed изменяется внутри callback. Такой подход интуитивен, но для фильтрации лучше подходит filter.
2. Создание нового массива на основе существующего (не рекомендуется)
const numbers = [1, 2, 3];
const doubled = [];
numbers.forEach(num => doubled.push(num * 2));
console.log(doubled);
[2, 4, 6]
Хотя это работает, метод map специально предназначен для таких задач и возвращает новый массив без побочных эффектов.
3. Использование thisArg для привязки контекста
const items = [1, 2, 3];
const accumulator = {
sum: 0,
add: function(num) {
this.sum += num;
}
};
items.forEach(function(num) {
this.add(num);
}, accumulator);
console.log(accumulator.sum);
6
Здесь второй аргумент accumulator становится this внутри callback. Стрелочная функция не подойдёт.
4. Обход двумерного массива
const matrix = [
[1, 2],
[3, 4],
[5, 6]
];
matrix.forEach((row, rowIndex) => {
row.forEach((cell, colIndex) => {
console.log(`[${rowIndex}][${colIndex}] = ${cell}`);
});
});
[0][0] = 1 [0][1] = 2 [1][0] = 3 [1][1] = 4 [2][0] = 5 [2][1] = 6
Вложенные forEach удобны для плоских матриц, но для многомерных массивов лучше использовать рекурсию.
5. Обход и удаление элементов (опасный приём)
const arr = [1, 2, 3, 4, 5];
arr.forEach((num, index) => {
if (num % 2 === 0) {
arr.splice(index, 1);
}
});
console.log(arr);
[1, 3, 5]
Результат может быть верным для данного примера, но при большем количестве чётных элементов подряд возникнут пропуски, так как индексы смещаются. Лучше использовать filter.
6. Комбинация forEach с объектом Map
const map = new Map([['a', 1], ['b', 2]]);
// Массив из Map не является итерируемым напрямую, но можно получить entries
[...map.entries()].forEach(([key, value]) => {
console.log(`${key}: ${value}`);
});
a: 1 b: 2
Spread-оператор превращает Map в массив массивов пар, затем forEach обрабатывает их.
7. Использование forEach для отладки логов
const users = [
{ name: 'Анна', age: 25 },
{ name: 'Борис', age: 30 },
{ name: 'Виктор', age: 35 }
];
users.forEach(user => console.log(`${user.name}: ${user.age} лет`));
Анна: 25 лет Борис: 30 лет Виктор: 35 лет
Простой способ вывести данные для отладки без создания дополнительного массива.
8. forEach с передачей исходного массива (третий параметр)
const letters = ['a', 'b', 'c'];
letters.forEach((letter, index, arr) => {
console.log(`Элемент ${letter} находится в массиве ${arr.join(',')} на позиции ${index}`);
});
Элемент a находится в массиве a,b,c на позиции 0 Элемент b находится в массиве a,b,c на позиции 1 Элемент c находится в массиве a,b,c на позиции 2
Третий параметр редко нужен, но может пригодиться для доступа к массиву из вложенной функции, когда нет внешней ссылки.