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 ітерує ітероване, то спершу викликає метод ітерованого [@@iterator](), котрий повертає ітератор, а потім раз за разом викликає метод результівного ітератора next(), аби отримати послідовність значень, що по черзі присвоюються variable.

Вихід з циклу for...of відбувається, коли завершується ітератор (коли метод ітератора next() повертає об'єкт, що містить done: true). Також для зміни звичайного ходу виконання можна використовувати інструкції контролю ходу виконання. break спричиняє вихід з циклу і перехід до першої інструкції після тіла циклу, натомість continue призводить до пропуску решти інструкцій поточної ітерації та переходу до наступної ітерації.

Якщо з циклу 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");
}

Ітерування ітерованого, визначеного користувачем

Ітерування об'єкта з методом @@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

Ітерування об'єкта з генераторним методом @@iterator:

const iterable = {
  *[Symbol.iterator]() {
    yield 1;
    yield 2;
    yield 3;
  },
};
for (const value of iterable) {
  console.log(value);
}
// 1
// 2
// 3

Ітеровані ітератори (ітератори з методом [@@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
Chrome Edge Firefox Internet Explorer Opera Safari WebView Android Chrome Android Firefox for Android Opera Android Safari on iOS Samsung Internet Deno Node.js
for...of
Chrome Full support 38
Edge Full support 12
Firefox Full support 13
footnote
Internet Explorer No support No
Opera Full support 25
Safari Full support 7
WebView Android Full support 38
Chrome Android Full support 38
Firefox for Android Full support 14
footnote
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 No
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 No
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

Дивіться також