Please note, this is a STATIC archive of website developer.mozilla.org from November 2016, cach3.com does not collect or store any user information, there is no "phishing" involved.

The Iterator protocol

Перевод не завершен. Пожалуйста, помогите перевести эту статью с английского.

Это экспериментальная технология, часть предложения Harmony (ECMAScript 6).
Поскольку спецификация этой технологии ещё не стабилизировалась, проверьте таблицу совместимости её использования в различных браузерах. Также обратите внимание, что синтаксис и поведение экспериментальной технологии могут быть изменены в будущих версиях браузеров в соответствии с изменениями в спецификации.

Одно из нововведений стандарта ECMAScript 6 - протоколы перебора, которые могут реализованы любым объектом, соблюдая при этом определенные правила.

Протоколы перебора

Протоколы перебора включают the "iterable" protocol и the "iterator" protocol.

Протокол "Итерируемый"

Протокол "Итерируемый" позволяет JavaScript объектам определять или настраивать поведение перебора, например, то какие значения перебираются в конструкции for..of. Некоторые встроенные типы, такие как Array или Map, имеют поведение перебора по умолчанию, в то время как другие типы (такие как Object) его не имеют

Для того, чтобы объект был итерируемым, в нем должен быть реализован метод @@iterator, т.е. этот объект (или любой из объектов из его prototype chain) должен иметь свойство с именем Symbol.iterator:

Свойство Значение
[Symbol.iterator] Функция без аргументов, возвращающая объект, соответствующий iterator protocol.

Всякий раз, когда объект подлежит перебору (например, когда в коде встречается цикл for..of), вызывается его метод @@iterator без аргументов, и возвращаемый iterator используется для получения перебираемых значений.

Протокол "Итератор"

Протокол "Итератор" определяет стандартный способ получения последовательности значений  (конечной или бесконечной).

Объект является итератором, если в нем определен метод next() , реализующий следующую логику:

Свойство Значение
next

Функция без аргументов, возвращающая объект с двумя свойствами:

  • done (boolean)
    • Принимает значение true если итератор достиг конца итерируемой последовательности. В этом случае свойство value может определять возвращаемое значение итератора. Возвращаемые значения объясняются более подробно here.
    • Принимает значение false если итератор может генерировать следующее значение последовательности. Это эквивалентно не указанному done.
  • value - любое JavaScript значение, возвращаемое итератором. Может быть опущено, если done имеет значение true.

Некоторые итераторы, в свою очередь, итерабельны:

var someArray = [1, 5, 7];
var someArrayEntries = someArray.entries();

someArrayEntries.toString();           // "[object Array Iterator]"
someArrayEntries === someArrayEntries[Symbol.iterator]();    // true

Примеры использования протокола "итератора"

String является примером встроенного итерабельного объекта:

var someString = "hi";
typeof someString[Symbol.iterator]           // "function"

По умолчанию итератор строки возвращает символы строки друг за другом:

var iterator = someString[Symbol.iterator]();
iterator + ""                                // "[object String Iterator]"
 
iterator.next()                              // { value: "h", done: false }
iterator.next()                              // { value: "i", done: false }
iterator.next()                              // { value: undefined, done: true }

Some built-in constructs, such as the spread operator, use the same iteration protocol under the hood:

[...someString]                              // ["h", "i"]

Поведение итератора можно переопределить применив собственный @@iterator:

var someString = new String("hi");          // need to construct a String object explicitly to avoid auto-boxing

someString[Symbol.iterator] = function() {
  return { // this is the iterator object, returning a single element, the string "bye"
    next: function() {
      if (this._first) {
        this._first = false;
        return { value: "bye", done: false };
      } else {
        return { done: true };
      }
    },
    _first: true
  };
};

Notice how redefining @@iterator affects the behavior of built-in constructs, that use the iteration protocol:

[...someString]                              // ["bye"]
someString + ""                              // "hi"

Builtin iterables

String, Array, TypedArray, Map and Set are all built-in iterables, because the prototype objects of them all have an @@iterator method.

User-defined iterables

We can make our own iterables like this:

var myIterable = {}
myIterable[Symbol.iterator] = function* () {
    yield 1;
    yield 2;
    yield 3;
};
[...myIterable] // [1, 2, 3]

Builtin APIs need iterables

Map([iterable]), WeakMap([iterable]), Set([iterable]) and WeakSet([iterable]):

var myObj = {}
new Map([[1,"a"],[2,"b"],[3,"c"]]).get(2)               // "b"
new WeakMap([[{},"a"],[myObj,"b"],[{},"c"]]).get(myObj) // "b"
new Set([1, 2, 3]).has(3)                               // true
new Set("123").has("2")                                 // true
new WeakSet(function*() {
    yield {};
    yield myObj;
    yield {};
}()).has(myObj)                                     // true

and Promise.all(iterable), Promise.race(iterable), Array.from()

Syntaxes need iterables

for-ofspread, yield*, destructing

for(let value of ["a", "b", "c"]){
    console.log(value)
}
// "a"
// "b"
// "c"

[..."abc"] // ["a", "b", "c"]

function* gen(){
  yield* ["a", "b", "c"]
}

gen().next() // { value:"a", done:false }

[a, b, c] = new Set(["a", "b", "c"])
a // "a"

Non-well-formed iterables

If an iterable's @@iterator method doesn't return an iterator object, then it's a non-well-formed iterable, using it as such is likely to result in runtime exceptions or buggy behavior:

var nonWellFormedIterable = {}
nonWellFormedIterable[Symbol.iterator] = () => 1
[...nonWellFormedIterable] // TypeError: [] is not a function

A generator object is an iterator or an iterable?

The answer is, both are correct:

var aGeneratorObject = function*(){
    yield 1;
    yield 2;
    yield 3;
}()
typeof aGeneratorObject.next                
// "function", because it has a next method, so it's an iterator
typeof aGeneratorObject[Symbol.iterator]    
// "function", because it has an @@iterator method, so it's an iterable
aGeneratorObject[Symbol.iterator]() === aGeneratorObject  
// true, because its @@iterator method return its self (an iterator), so it's an well-formed iterable
[...aGeneratorObject]                       
// [1, 2, 3]

Examples

Simple iterator

function makeIterator(array){
    var nextIndex = 0;
    
    return {
       next: function(){
           return nextIndex < array.length ?
               {value: array[nextIndex++], done: false} :
               {done: true};
       }
    }
}

var it = makeIterator(['yo', 'ya']);

console.log(it.next().value); // 'yo'
console.log(it.next().value); // 'ya'
console.log(it.next().done);  // true

Infinite iterator

function idMaker(){
    var index = 0;
    
    return {
       next: function(){
           return {value: index++, done: false};
       }
    }
}

var it = idMaker();

console.log(it.next().value); // '0'
console.log(it.next().value); // '1'
console.log(it.next().value); // '2'
// ...

With a generator

function* makeSimpleGenerator(array){
    var nextIndex = 0;
    
    while(nextIndex < array.length){
        yield array[nextIndex++];
    }
}

var gen = makeSimpleGenerator(['yo', 'ya']);

console.log(gen.next().value); // 'yo'
console.log(gen.next().value); // 'ya'
console.log(gen.next().done);  // true



function* idMaker(){
    var index = 0;
    while(true)
        yield index++;
}

var gen = idMaker();

console.log(gen.next().value); // '0'
console.log(gen.next().value); // '1'
console.log(gen.next().value); // '2'
// ...

For more informations on ES6 generators, see the dedicated page.

Метки документа и участники

 Внесли вклад в эту страницу: asaskevich, petanisimov, fscholz, hindmost
 Обновлялась последний раз: asaskevich,