for...of

Сводка

Оператор for...of выполняет цикл обхода итерируемых объектов (включая Array, Map, Set, объект аргументов и подобных), вызывая на каждом шаге итерации операторы для каждого значения из различных свойств объекта.

Синтаксис

for (variable of iterable) {
  statement
}
variable

На каждом шаге итерации variable присваивается значение нового свойства объекта iterable. Переменная variable может быть также объявлена с помощью const, let или var.

iterable

Объект, перечисляемые свойства которого обходятся во время выполнения цикла.

Примеры

Обход Array

let iterable = [10, 20, 30];

for (let value of iterable) {
  value += 1;
  console.log(value);
}
// 11
// 21
// 31

Можно также использовать const вместо let, если не нужно переназначать переменные внутри блока.

let iterable = [10, 20, 30];

for (const value of iterable) {
  console.log(value);
}
// 10
// 20
// 30

Обход String

let iterable = 'boo';

for (let value of iterable) {
  console.log(value);
}
// "b"
// "o"
// "o"

Обход TypedArray

let iterable = new Uint8Array([0x00, 0xff]);

for (let value of iterable) {
  console.log(value);
}
// 0
// 255

Обход Map

let iterable = new Map([['a', 1], ['b', 2], ['c', 3]]);

for (let entry of iterable) {
  console.log(entry);
}
// ['a', 1]
// ['b', 2]
// ['c', 3]

for (let [key, value] of iterable) {
  console.log(value);
}
// 1
// 2
// 3

Обход Set

let iterable = new Set([1, 1, 2, 2, 3, 3]);

for (let value of iterable) {
  console.log(value);
}
// 1
// 2
// 3

Обход объекта arguments

(function() {
  for (let argument of arguments) {
    console.log(argument);
  }
})(1, 2, 3);

// 1
// 2
// 3

Обход DOM коллекций

Обход DOM коллекций наподобие NodeList: следующий пример добавляет класс read параграфам, являющимся непосредственными потомками статей:

// Примечание: работает только на платформах, где
// реализован NodeList.prototype[Symbol.iterator]
let articleParagraphs = document.querySelectorAll('article > p');

for (let paragraph of articleParagraphs) {
  paragraph.classList.add('read');
}

Закрытие итераторов

В циклах for...of аварийный выход осуществляется через break, throw или return. Во всех вариантах итератор завершается.

function* foo(){
  yield 1;
  yield 2;
  yield 3;
};

for (let o of foo()) {
  console.log(o);
  break; //  итератор закрывается, возврат
}

Обход генераторов

Вы можете выполнять обход генераторов, вот пример:

js
function* fibonacci() {
  // функция-генератор
  let [prev, curr] = [0, 1];
  for (;;) {
    [prev, curr] = [curr, prev + curr];
    yield curr;
  }
}

for (let n of fibonacci()) {
  // ограничивает последовательность на 1000
  if (n > 1000) break;
  console.log(n);
}

Не пытайтесь повторно использовать генератор

Генераторы нельзя использовать дважды, даже если цикл for...of завершится аварийно, например, через оператор break . При выходе из цикла генератор завершается, и любые попытки получить из него значение обречены.

js
var gen = (function* () {
  yield 1;
  yield 2;
  yield 3;
})();
for (let o of gen) {
  console.log(o);
  break; // Завешение обхода
}

// Генератор нельзя повторно обойти, следующее не имеет смысла!
for (let o of gen) {
  console.log(o); // Не будет вызван
}

Обход итерируемых объектов

Кроме того, можно сделать обход объекта, явно реализующего iterable:

var iterable = {
  [Symbol.iterator]() {
    return {
      i: 0,
      next() {
        if (this.i < 3) {
          return { value: this.i++, done: false };
        }
        return { value: undefined, done: true };
      }
    };
  }
};

for (var value of iterable) {
  console.log(value);
}
// 0
// 1
// 2

Различия между 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() {};

let iterable = [3, 5, 7];
iterable.foo = 'hello';

for (let i in iterable) {
  console.log(i); // выведет 0, 1, 2, "foo", "arrCustom", "objCustom"
}

for (let i in iterable) {
  if (iterable.hasOwnProperty(i)) {
    console.log(i); // выведет 0, 1, 2, "foo"
  }
}

for (let i of iterable) {
  console.log(i); // выведет 3, 5, 7
}

Разберёмся шаг за шагом в вышеописанном коде.

Object.prototype.objCustom = function() {};
Array.prototype.arrCustom = function() {};

let iterable = [3, 5, 7];
iterable.foo = 'hello';

Каждый объект унаследует метод objCustom и каждый массив Array унаследует метод arrCustom благодаря созданию их в Object.prototype и Array.prototype. Объект iterable унаследует методы objCustom и arrCustom из-за наследования через прототип.

for (let i in iterable) {
  console.log(i); // выведет 0, 1, 2, "foo", "arrCustom", "objCustom"
}

Цикл выводит только перечисляемые свойства объекта iterable, в порядке их создания. Он не выводит значения 3, 5, 7 и hello поскольку они не являются перечисляемыми, фактически они вообще не являются свойствами, они являются значениями. Выводятся же имена свойств и методов, например arrCustom и objCustom. Если вы ещё не совсем поняли, по каким свойствам осуществляется обход, вот дополнительное объяснение того, как работает array iteration and for...in .

for (let i in iterable) {
  if (iterable.hasOwnProperty(i)) {
    console.log(i); // выведет 0, 1, 2, "foo"
  }
}

Цикл аналогичен предыдущему, но использует hasOwnProperty() для проверки того, собственное ли это свойство объекта или унаследованное. Выводятся только собственные свойства. Имена 0, 1, 2 и foo принадлежат только экземпляру объекта (не унаследованы). Методы arrCustom и objCustom не выводятся, поскольку они унаследованы.

for (let i of iterable) {
  console.log(i); // выведет 3, 5, 7
}

Этот цикл обходит iterable и выводит те значения итерируемого объекта, которые определены в способе его перебора, т.е. не свойства объекта, а значения массива 3, 5, 7 .

Спецификации

Specification
ECMAScript Language Specification
# sec-for-in-and-for-of-statements

Совместимость с браузерами

BCD tables only load in the browser

Смотрите также