for...of
Інструкція for...of
виконує цикл, що обробляє послідовність значень, отриманих з ітерованого об'єкта. До ітерованих об'єктів належать екземпляри вбудованих типів, як то Array
, String
, TypedArray
, Map
, Set
, NodeList
(та інших колекцій DOM), а також об'єкт arguments
, породжені генераторними функціями генератори й визначені користувачем ітеровані об'єкти.
Спробуйте його в дії
Синтаксис
for (variable of iterable)
statement;
variable
На кожній ітерації отримує значення з послідовності. Повинно бути або оголошенням з
const
,let
чиvar
, або ціллю присвоєння (наприклад, попередньо визначеною змінною, властивістю об'єкта чи патерном присвоєння з деструктуруванням). Змінні, оголошені зvar
, не є локальними щодо циклу, тобто перебувають в тій же області видимості, що й циклfor...of
.iterable
Ітерований об'єкт. Джерело послідовності значень, котрі обробляє цикл.
statement
Інструкція, котра буде виконана на кожній ітерації. Може звертатися до
variable
. Для виконання декількох інструкцій можна застосувати блокову інструкцію.
Опис
Цикл for...of
обробляє значення, одне за одним отримані з ітерованого. Кожна обробка значення циклом зветься ітерацією, а цикл називають ітеруванням ітерованого. Кожна ітерація виконує інструкції, що можуть звертатися до поточного значення з послідовності.
Коли for...of
ітерує ітероване, то спершу викликає метод ітерованого [Symbol.iterator]()
, котрий повертає ітератор, а потім раз за разом викликає метод результівного ітератора next()
, аби отримати послідовність значень, що по черзі присвоюються variable
.
Вихід з циклу for...of
відбувається, коли завершується ітератор (коли метод ітератора next()
повертає об'єкт, що містить done: true
). Як і у випадку інших інструкцій циклів, всередині statement
можна користуватися інструкціями керування плином виконання:
break
зупиняє виконанняstatement
і переходить до першої інструкції після циклу.continue
зупиняє виконанняstatement
і переходить до наступної ітерації циклу.
Якщо з циклу for...of
відбувається ранній вихід (наприклад, якщо зустрілася інструкція break
чи була викинута помилка), то викликається метод return()
, щоб виконати прибирання.
Частина for...of
variable
приймає що завгодно, що може стояти перед оператором =
. Для оголошення змінної можна використовувати const
, якщо їй в тілі циклу не присвоюється нове значення (значення може змінюватися між ітераціями, адже це будуть окремі змінні). Інакше – можна застосувати let
.
const iterable = [10, 20, 30];
for (let value of iterable) {
value += 1;
console.log(value);
}
// 11
// 21
// 31
Примітка: Кожна ітерація породжує нову змінну. Присвоєння змінній нового значення всередині тіла циклу не вплине на вихідне значення ітерованого (в цьому випадку – масиву).
Деструктурування можна використати для присвоєння кількох локальних змінних, або використати аксесор властивості, як от for (x.y of iterable)
, щоб присвоїти значення властивості об'єкта.
Проте особливе правило забороняє використовувати як ім'я змінної async
. Наступний синтаксис – недійсний:
let async;
for (async of [1, 2, 3]); // SyntaxError: The left-hand side of a for-of loop may not be 'async'.
Таке правило запроваджено для уникнення неоднозначності синтаксису щодо дійсного коду for (async of => {};;)
, котрий позначає цикл for
.
Приклади
Ітерування Array
const iterable = [10, 20, 30];
for (const value of iterable) {
console.log(value);
}
// 10
// 20
// 30
Ітерування рядка
Рядки є ітерованими за кодовими точками Unicode.
const iterable = "boo";
for (const value of iterable) {
console.log(value);
}
// "b"
// "o"
// "o"
Ітерування TypedArray
const iterable = new Uint8Array([0x00, 0xff]);
for (const value of iterable) {
console.log(value);
}
// 0
// 255
Ітерування Map
const iterable = new Map([
["а", 1],
["б", 2],
["в", 3],
]);
for (const entry of iterable) {
console.log(entry);
}
// ['а', 1]
// ['б', 2]
// ['в', 3]
for (const [key, value] of iterable) {
console.log(value);
}
// 1
// 2
// 3
Ітерування Set
const iterable = new Set([1, 1, 2, 2, 3, 3]);
for (const value of iterable) {
console.log(value);
}
// 1
// 2
// 3
Ітерування об'єкта arguments
Щоб дослідити усі параметри, передані до функції, можна виконати ітерування об'єкта arguments
:
function foo() {
for (const value of arguments) {
console.log(value);
}
}
foo(1, 2, 3);
// 1
// 2
// 3
Ітерування NodeList
Наступний приклад додає клас read
до абзаців, що є безпосередніми нащадками елемента <article>
, шляхом ітерування колекції DOM NodeList
.
const articleParagraphs = document.querySelectorAll("article > p");
for (const paragraph of articleParagraphs) {
paragraph.classList.add("read");
}
Ітерування ітерованого, визначеного користувачем
Ітерування об'єкта з методом [Symbol.iterator]()
, що повертає самописний ітератор:
const iterable = {
[Symbol.iterator]() {
let i = 1;
return {
next() {
if (i <= 3) {
return { value: i++, done: false };
}
return { value: undefined, done: true };
},
};
},
};
for (const value of iterable) {
console.log(value);
}
// 1
// 2
// 3
Ітерування об'єкта з генераторним методом [Symbol.iterator]()
:
const iterable = {
*[Symbol.iterator]() {
yield 1;
yield 2;
yield 3;
},
};
for (const value of iterable) {
console.log(value);
}
// 1
// 2
// 3
Ітеровані ітератори (ітератори з методом [Symbol.iterator]()
, що повертає this
) є доволі поширеним підходом, щоб зробити ітератори застосовними в синтаксичних конструкціях, що очікують на ітеровані об'єкти, як то for...of
.
let i = 1;
const iterator = {
next() {
if (i <= 3) {
return { value: i++, done: false };
}
return { value: undefined, done: true };
},
[Symbol.iterator]() {
return this;
},
};
for (const value of iterator) {
console.log(value);
}
// 1
// 2
// 3
Ітерування генератора
function* source() {
yield 1;
yield 2;
yield 3;
}
const generator = source();
for (const value of generator) {
console.log(value);
}
// 1
// 2
// 3
Ранній вихід
Виконання інструкції break
в першому циклі призведе до раннього виходу. Ітератор ще не завершений, тож другий цикл продовжить там, де зупинився перший.
const source = [1, 2, 3];
const iterator = source[Symbol.iterator]();
for (const value of iterator) {
console.log(value);
if (value === 1) {
break;
}
console.log("Цей рядок не буде виведений в консоль.");
}
// 1
// Іще один цикл, що використовує той самий ітератор,
// продовжує там, де зупинився попередній цикл.
for (const value of iterator) {
console.log(value);
}
// 2
// 3
// Ітератор вичерправся.
// Цей цикл не виконає жодних ітерацій.
for (const value of iterator) {
console.log(value);
}
// [Жодного виводу]
Генератори реалізовують метод return()
, котрий змушує генераторну функцію виконувати раннє повернення, коли відбувається вихід з циклу. Це робить генератори непридатними для повторного використання в наступних циклах.
function* source() {
yield 1;
yield 2;
yield 3;
}
const generator = source();
for (const value of generator) {
console.log(value);
if (value === 1) {
break;
}
console.log("Цей рядок не буде виведений в консоль.");
}
// 1
// Генератор вичерпався.
// Цей цикл не виконає жодних ітерацій.
for (const value of generator) {
console.log(value);
}
// [Жодного виводу]
Різниця між for...of і for...in
Як інструкція for...in
, так інструкція for...of
– щось ітерують. Основна різниця полягає в тому, що саме вони ітерують.
Інструкція for...in
ітерує перелічувані рядкові властивості об'єкта, натомість інструкція for...of
ітерує значення, котрі ітерований об'єкт визначає для ітерування.
Наступний приклад демонструє різницю між циклом for...of
та циклом for...in
при застосуванні на Array
.
Object.prototype.objCustom = function () {};
Array.prototype.arrCustom = function () {};
const iterable = [3, 5, 7];
iterable.foo = "агов";
for (const i in iterable) {
console.log(i);
}
// "0", "1", "2", "foo", "arrCustom", "objCustom"
for (const i in iterable) {
if (Object.hasOwn(iterable, i)) {
console.log(i);
}
}
// "0" "1" "2" "foo"
for (const i of iterable) {
console.log(i);
}
// 3 5 7
Об'єкт iterable
успадковує властивості objCustom
та arrCustom
, адже має у своєму ланцюжку прототипів і Object.prototype
, і Array.prototype
.
Цикл for...in
виводить лише перелічувані властивості об'єкта iterable
. Він не виводить елементи масиву, 3
, 5
, 7
чи "агов"
, бо вони не є властивостями: вони є значеннями. Він виводить індекси масиву, так само як arrCustom
і objCustom
, що є справжніми властивостями. Якщо немає певності в тому, чому ітеруються саме ці властивості — є більш поглиблене роз'яснення, як працюють ітерування масиву та цикл for...in
.
Другий цикл – подібний до першого, але він використовує Object.hasOwn()
, аби перевіряти, чи є знайдена перелічувана властивість власною властивістю об'єкта, тобто не успадкованою. Якщо це так, то властивість виводиться. Властивості 0
, 1
, 2
і foo
– виводяться, тому що є власними. Властивості arrCustom
і objCustom
не виводяться, бо є успадкованими.
Цикл for...of
ітерує й виводить значення, котрі об'єкт iterable
, як масив (а масиви є ітерованими), визначає для ітерування. Демонструються елементи об'єкта – 3
, 5
, 7
, але жодна з властивостей об'єкта.
Специфікації
Сумісність із браузерами
desktop | mobile | server | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
for...of
|
Chrome Full support 38 | Edge Full support 12 | Firefox Full support 13 | Internet Explorer No support Ні | Opera Full support 25 | Safari Full support 7 | WebView Android Full support 38 | Chrome Android Full support 38 | Firefox for Android Full support 14 | Opera Android Full support 25 | Safari on iOS Full support 7 | Samsung Internet Full support 3.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |
async iterators
|
Chrome Full support 63 | Edge Full support 12 | Firefox Full support 57 | Internet Explorer No support Ні | Opera Full support 50 | Safari Full support 7 | WebView Android Full support 63 | Chrome Android Full support 63 | Firefox for Android Full support 57 | Opera Android Full support 46 | Safari on iOS Full support 7 | Samsung Internet Full support 8.0 | Deno Full support 1.0 | Node.js Full support 10.0.0 |
Closing iterators
|
Chrome Full support 51 | Edge Full support 14 | Firefox Full support 53 | Internet Explorer No support Ні | Opera Full support 38 | Safari Full support 7 | WebView Android Full support 51 | Chrome Android Full support 51 | Firefox for Android Full support 53 | Opera Android Full support 41 | Safari on iOS Full support 7 | Samsung Internet Full support 5.0 | Deno Full support 1.0 | Node.js Full support 6.5.0 |