Статьи

Испытание ECMAScript.next для … цикла в Firefox 13


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__ выше).

Связанное чтение

  1. Перебор массивов и объектов в JavaScript
  2. Ошибка 699565 — Реализация гармонии для петель
  3. для … of — MDN