ECMAScript.next
for … of loop был создан на основе прототипа в Firefox 13. Это грубая первая реализация, которая еще не завершена. Этот пост в блоге объясняет, как это работает.
Перебор массивов
Стандартный цикл for … in имеет несколько особенностей: он перебирает все перечисляемые свойства объекта, включая унаследованные. Это означает, что он не подходит для итерации по массивам, потому что он не итерирует по элементам массива. Вы даже не можете использовать его для перебора индексов массива, потому что всегда включаются неиндексированные имена свойств. В качестве примера возьмем следующий массив.
let arr = [ "blue", "green" ]; arr.notAnIndex = 123; Array.prototype.protoProp = 456;
Если вы выполните итерацию с помощью for … in, вы получите:
> for(k in arr) console.log(k) 0 1 notAnIndex protoProp
Следовательно, для ECMAScript 5 лучше использовать Array.prototype.forEach
[1] . ECMAScript.next предоставит нам цикл for …, который также работает, как и ожидалось:
> for(k of arr) console.log(k) blue green
итераторы
Итераторы ECMCAScript.next
позволяют реализовать собственную стратегию итерации для структуры данных. Чтобы добиться того же в ECMAScript 5, обычно создается новый массив и итерируется по нему с помощью Array.prototype.forEach (). Например, Object.keys () можно рассматривать как настраиваемую стратегию итерации для объектов: он выполняет итерацию по перечисляемым именам собственных свойств. Однако каждый раз, когда он вызывается, он создает массив с именами. Итератор делает это проще. Ниже приведен пример объекта, который поставляется с пользовательской итерацией.
let obj = { data: [ "hello", "world" ], // Custom iteration: __iterator__: function () { let index = 0; let that = this; // Return iterator object return { next: function () { if (index < that.data.length) { return that.data[index++]; } else { throw StopIteration; } } } } }
Специальный метод __iterator__ возвращает объект итератора. Такой объект имеет метод next (), который либо возвращает следующий элемент в текущей последовательности итерации, либо выдает StopIteration, если элементов больше нет. Firefox 13 for … of пока не поддерживает метод __iterator__. Как только это произойдет, вы сможете перебирать obj как это:
> for (x of obj) console.log(x); hello world
Обратите внимание, что окончательная версия for … of, вероятно, будет использовать специальный механизм для указания имени метода итератора. То есть у него не будет имени __iterator__.
Генераторы
Помимо прочего,
генераторы помогают в реализации итераторов. Вышеупомянутый итератор может быть реализован следующим образом через генератор:
let obj = { data: [ "hello", "world" ], // function* means: generator __iterator__: function* generator() { for(let index=0; index < this.data.length; index++) { yield this.data[index]; } } }
Давайте использовать генератор, чтобы перебрать записи объекта [имя свойства, значение свойства].
function* items(obj) { // Firefox 13: function items... for(let key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { yield [ key, obj[key] ]; } } }
Приведенный выше код работает в Firefox 13, но вы должны опустить функцию * after. Вы используете items () следующим образом:
> let obj = { first: "Jane", last: "Doe" }; > for (x of items(obj)) console.log(x); ["first", "Jane"] ["last", "Doe"]
Вы также можете деструктурировать массив, если вас интересуют ключи и значения:
> for ([k,v] of items(obj)) console.log(k+" = "+v); first = Jane last = Doe
До сих пор отсутствует
Когда for … of полностью реализован, вы должны иметь возможность напрямую перебирать объекты:
> let obj = { first: "Jane", last: "Doe" }; > for ([k,v] of obj) console.log(k+" = "+v); first = Jane last = Doe
И будет поддержка реализации пользовательской итерации для любого объекта (аналогично гипотетическому методу __iterator__ выше).