Сводка
Оператор 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; // итератор закрывается, возврат
}
Обход генераторов
Вы можете выполнять обход генераторов, вот пример:
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 . При выходе из цикла генератор завершается, и любые попытки получить из него значение обречены.
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 .
Спецификации
| Спецификация | Статус | Комментарий |
|---|---|---|
| ECMAScript 2015 (6th Edition, ECMA-262) Определение 'for...of statement' в этой спецификации. |
Стандарт | Изначальное опеределение. |
| ECMAScript (ECMA-262) Определение 'for...of statement' в этой спецификации. |
Живой стандарт |
Совместимость с браузерами
| Компьютеры | Мобильные | Server | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
for...of | Chrome Полная поддержка 38 | Edge Полная поддержка 12 | Firefox
Полная поддержка
13
| IE Нет поддержки Нет | Opera Полная поддержка 25 | Safari Полная поддержка 7 | WebView Android Полная поддержка 38 | Chrome Android Полная поддержка 38 | Firefox Android
Полная поддержка
14
| Opera Android Полная поддержка 25 | Safari iOS Полная поддержка 7 | Samsung Internet Android Полная поддержка 3.0 | nodejs Полная поддержка 0.12 |
| async iterators | Chrome Полная поддержка 63 | Edge Полная поддержка 12 | Firefox Полная поддержка 57 | IE Нет поддержки Нет | Opera Полная поддержка 50 | Safari Полная поддержка 7 | WebView Android Полная поддержка 63 | Chrome Android Полная поддержка 63 | Firefox Android Полная поддержка 57 | Opera Android Полная поддержка 46 | Safari iOS Полная поддержка 7 | Samsung Internet Android Полная поддержка 8.0 | nodejs Полная поддержка 10.0.0 |
| Closing iterators | Chrome Полная поддержка 51 | Edge Полная поддержка 14 | Firefox Полная поддержка 53 | IE Нет поддержки Нет | Opera Полная поддержка 38 | Safari Полная поддержка 7 | WebView Android Полная поддержка 51 | Chrome Android Полная поддержка 51 | Firefox Android Полная поддержка 53 | Opera Android Полная поддержка 41 | Safari iOS Полная поддержка 7 | Samsung Internet Android Полная поддержка 5.0 | nodejs Полная поддержка 6.5.0 |
Легенда
- Полная поддержка
- Полная поддержка
- Нет поддержки
- Нет поддержки
- Смотрите замечания реализации.
- Смотрите замечания реализации.