Array.prototype.forEach()
Метод forEach()
("для кожного") виконує дану функцію один раз для кожного елементу масиву.
Спробуйте його в дії
Синтаксис
forEach(callbackFn)
forEach(callbackFn, thisArg)
Параметри
callbackFn
Функція до виконання для кожного елемента масиву. Її повернене значення - відкидається. Ця функція викликається із наступними аргументами:
element
Поточний елемент масиву, який опрацьовується.
index
Індекс поточного елемента масиву, що опрацьовується.
array
Власне масив, на якому було викликано
forEach()
.
thisArg
Необов'язковеЗначення для використання за
this
при виконанніcallbackFn
. Докладніше про це в ітеративних методах.
Результат
undefined
.
Опис
Метод forEach()
є ітеративним методом. Він викликає передану функцію callbackFn
на кожному елементі масиву, в порядку зростання індексів. На відміну від map()
, forEach()
завжди повертає undefined
і не може бути проміжною ланкою ланцюжка. Типове його використання – для виконання побічних ефектів у кінці ланцюжка.
callbackFn
закликається лише для тих індексів масиву, що мають присвоєні значення. Вона не закликається для порожніх комірок у розріджених масивах.
forEach()
не видозмінює масиву, на котрому його викликали, але функція, передана як callbackFn
, може це робити. Проте слід звернути увагу, що довжина масиву запам'ятовується до першого заклику callbackFn
. Таким чином:
callbackFn
не оброблятиме жодних елементів, доданих поза початковою довжиною масиву, відколи почався викликforEach()
.- Зміни за вже обробленими індексами не призводять до повторного заклику на них
callbackFn
. - Якщо наявний, іще не оброблений елемент масиву вже був змінений
callbackFn
, то його значення, передане вcallbackFn
, буде значенням на ту мить, коли такий елемент обробляється. Видалені елементи – не обробляються.
Застереження: Одночасні модифікації такого типу, як описано в попередньому абзаці, часто приводять до коду, який важко зрозуміти. Загалом заведено уникати такого запису (крім особливих випадків).
Метод forEach()
є узагальненим. Він лишень очікує, що значення this
матиме властивість length
, а також властивості з цілочисловими ключами.
Немає іншого способу зупинити чи перервати цикл forEach()
, окрім викидання винятку. Якщо потрібна логіка з перериванням, то метод forEach()
– негодящий інструмент.
Раннього завершення можна досягнути з інструкціями циклів, як то for
, for...of
і for...in
. Методи масивів, як то every()
, some()
, find()
і {{jsxref("Array/findIndex", "findIndex()")}, також відразу зупиняють ітерацію, коли подальше ітерування стає непотрібним.
forEach()
розраховує на синхронну функцію: він не очікує на проміси. Слід взяти до уваги можливі наслідки при використанні промісів (чи асинхронних функцій) у функціях зворотного виклику для forEach
.
const ratings = [5, 4, 5];
let sum = 0;
const sumFunction = async (a, b) => a + b;
ratings.forEach(async (rating) => {
sum = await sumFunction(sum, rating);
});
console.log(sum);
// Наївно очікуваний вивід: 14
// Фактичний вивід: 0
Про виконання низки асинхронних операцій, послідовно чи паралельно, дивіться композицію промісів.
Приклади
Використання forEach() на розріджених масивах
const arraySparse = [1, 3, /* пропуск */, 7];
let numCallbackRuns = 0;
arraySparse.forEach((element) => {
console.log({ element });
numCallbackRuns++;
});
console.log({ numCallbackRuns });
// { element: 1 }
// { element: 3 }
// { element: 7 }
// { numCallbackRuns: 3 }
Функція зворотного виклику не закликається для пропущеного значення за індексом 2.
Перетворення циклу for на forEach
const items = ["item1", "item2", "item3"];
const copyItems = [];
// до
for (let i = 0; i < items.length; i++) {
copyItems.push(items[i]);
}
// після
items.forEach((item) => {
copyItems.push(item);
});
Друк вмісту масиву
Примітка: Щоб показати вміст масиву у консолі, можна застосувати метод
console.table()
, який друкує відформатований варіант масиву.Приклад далі ілюструє альтернативний підхід, із застосуванням
forEach()
.
Наступний код друкує рядок для кожного елементу в масиві:
const logArrayElements = (element, index /*, array */) => {
console.log(`a[${index}] = ${element}`);
};
// Зауважте, що порядковий номер 2 пропущено, оскільки в масиві не існує
// елементу на цій позиції.
[2, 5, , 9].forEach(logArrayElements);
// Друкує:
// a[0] = 2
// a[1] = 5
// a[3] = 9
Застосування thisArg
Наступний (надуманий) приклад оновлює властивості об'єкта, з урахуванням поданих елементів масиву:
class Counter {
constructor() {
this.sum = 0;
this.count = 0;
}
add(array) {
// Лише вирази функцій матимуть власне зв'язування this
array.forEach(function countEntry(entry) {
this.sum += entry;
++this.count;
}, this);
}
}
const obj = new Counter();
obj.add([2, 5, 9]);
console.log(obj.count); // 3
console.log(obj.sum); // 16
Оскільки параметр thisArg
(this
) було задано в forEach()
, він передавався до функції callback
кожного разу, коли вона викликалась. Сама функція зворотного виклику використовує те саме значення this
.
Примітка: Якщо передачу функції зворотного виклику було використано із застосуванням виразу стрілкової функції, то значення параметра
thisArg
можна опустити, оскільки всі стрілкові функції прив'язують значенняthis
лексично.
Функція копіювання об'єкта
Наступний код створює копію поданого об'єкта.
Існують різні способи створити копію об'єкта. Спосіб, що наведено нижче - це лише один із них, покликаний показати, як працює Array.prototype.forEach()
шляхом використання службових функцій Object.*
.
const copy = (obj) => {
const copy = Object.create(Object.getPrototypeOf(obj));
const propNames = Object.getOwnPropertyNames(obj);
propNames.forEach((name) => {
const desc = Object.getOwnPropertyDescriptor(obj, name);
Object.defineProperty(copy, name, desc);
});
return copy;
};
const obj1 = { a: 1, b: 2 };
const obj2 = copy(obj1); // obj2 тепер має точнісінько такий самий вигляд, як obj1
Зміна масиву під час перебирання його елементів
Наступний приклад друкує one
, two
, four
.
Коли в процесі перебору елементів функція підходить до значення two
, перший елемент цілого масиву витісняється з нього. В результаті всі елементи, що залишилися, зміщуються на одну позицію вліво. Оскільки елемент four
тепер займає лівішу позицію, елемент three
пропускається.
Метод forEach()
не робить копію масиву перед перебиранням.
const words = ["one", "two", "three", "four"];
words.forEach((word) => {
console.log(word);
if (word === "two") {
words.shift(); // елемент 'one' видаляється з масиву
}
}); // one // two // four
console.log(words); // ['two', 'three', 'four']
Сплощення масиву
Приклад нижче наведено лише для навчальних потреб. Для сплощення масивів вбудованими методами можна застосовувати Array.prototype.flat()
.
const flatten = (arr) => {
const result = [];
arr.forEach((item) => {
if (Array.isArray(item)) {
result.push(...flatten(item));
} else {
result.push(item);
}
});
return result;
};
// Застосування
const nested = [1, 2, 3, [4, 5, [6, 7], 8, 9]];
console.log(flatten(nested)); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
Виклик forEach() на об'єктах-немасивах
Метод forEach()
зчитує з this
властивість length
, а потім звертається до кожної цілочислової властивості.
const arrayLike = {
length: 3,
0: 2,
1: 3,
2: 4,
};
Array.prototype.forEach.call(arrayLike, (x) => console.log(x));
// 2
// 3
// 4
Специфікації
Сумісність із браузерами
desktop | mobile | server | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
forEach
|
Chrome Full support 1 | Edge Full support 12 | Firefox Full support 1.5 | Internet Explorer Full support 9 | Opera Full support 9.5 | Safari Full support 3 | WebView Android Full support 37 | Chrome Android Full support 18 | Firefox for Android Full support 4 | Opera Android Full support 10.1 | Safari on iOS Full support 1 | Samsung Internet Full support 1.0 | Deno Full support 1.0 | Node.js Full support 0.10.0 |