Как цикл `for..of` разрешает итератор из объекта?

Чтобы объект реализовал итерируемый интерфейс, он должен реализовать ключ [Symbol.iterator], указывающий на функцию, возвращающую iterator. Мне интересно, вызывает ли цикл for..of этот метод внутри объекта, чтобы получить этот iterator?

Причина, по которой мне любопытно, заключается в том, что, например, Map определяет интерфейс с несколькими итераторами (записями, значениями, ключами), и кажется, что если не указано явно, цикл for..of использует итератор, возвращенный вызовом map.entries().

Я пытаюсь найти в спецификации, но указывает только, что iterator передается в качестве параметра абстрактной операции ForOf:

Абстрактная операция ForIn/OfBodyEvaluation вызывается с аргументами lhs, stmt, iterator, iterationKind, lhsKind и labelSet.

Итак, в основном два вопроса:

  1. Как итератор получается из объекта?
  2. Где это указано в спецификации?

person Max Koretskyi    schedule 10.10.2017    source источник
comment
Проверьте последний шаг Forin/OfHeadОценка   -  person Bergi    schedule 10.10.2017
comment
если не указано явно, цикл for..of использует итератор, возвращенный вызовом map.entries(). - не совсем так. Он всегда использует map[Symbol.iterator](), , который является тот же метод, что и entries. Если вы передаете объект итератора, он также вызывает …[Symbol.iterator]() для них, просто метод возвращает сам объект (return this;) для экземпляров итератора.   -  person Bergi    schedule 10.10.2017
comment
@Bergi, большое спасибо за подтверждение, я так и думал.   -  person Max Koretskyi    schedule 10.10.2017


Ответы (2)


Конкретное место, где указана операция, находится в 7.4. 1 GetIterator( объект [ , метод ] ). Это получает свойство @@iterator переданного объекта на шаге 1a. абстрактной операции:

а. Задайте для method значение GetMethod(объект, @@iterator).

@@iterator — это известный символ это свойство Symbol.iterator объектов.

Это используется циклами for-in и for-of из-за продукций в 13.7.5.11 Семантика времени выполнения:

IterationStatement : for(ForDeclaration of AssignmentExpression) Statement

  1. Пусть keyResult будет результатом выполнения ForIn/OfHeadEvaluation(BoundNames ForDeclaration, AssignmentExpression, итерация).
  2. Вернуть ForIn/OfBodyEvaluation(ForDeclaration, Statement, keyResult, iterate, lexicalBinding, labelSet ).

Здесь вы можете увидеть аргумент итератора, переданный в ForIn/OfBodyEvaluation — это возвращаемое значение keyResult ForIn/OfHeadEvaluation. Возвращаемое значение на шаге 7b:

б. Вернуть GetIterator(exprValue< /эм>).

Таким образом, циклы for-of получают итератор, обращаясь к известному символу @@iterator или Symbol.iterator по спецификации.

person Andrew Li    schedule 10.10.2017
comment
большое спасибо, это то, что я искал. GetIterator ( obj [ , method ] ) может опционально взять метод, а из 7b я вижу, что метод не указан, следовательно, Set method to ? GetMethod(obj, @@iterator).. Правильный? Также мне интересно, что это за вопросительный знак перед GetMethod. Любые идеи? - person Max Koretskyi; 10.10.2017
comment
@AngularInDepth.com Да. Передается только exprValue. - person Andrew Li; 10.10.2017
comment
@AngularInDepth.com Это описано здесь: stackoverflow.com/questions/41733976/ - person Andrew Li; 10.10.2017

Объект может определять только один символ Symbol.iterator, который будет вызываться при итерации самого объекта. Другие свойства объекта, такие как приведенные вами примеры (entries, keys, values), также могут возвращать итератор, но, как правило, это разные итераторы. Они могут быть одинаковыми, но это просто выбор реализации. Нет никакой двусмысленности относительно того, какой итератор вызывается при повторении объекта с помощью for..of. Это тот, который вернул [Symbol.iterator].

  1. Как итератор получается из объекта?

Вы можете получить его, вызвав функцию с ключом Symbol.iterator, например.

const iterator = obj[Symbol.iterator]();

Он извлекается неявно с помощью for..of.

  1. Где это указано в спецификации?

Эта таблица в спецификациях поясняет:

@@iterator Symbol.iterator

Метод, возвращающий итератор по умолчанию для объекта. Вызывается семантикой оператора for-of.

Вот как вы можете создать пользовательскую функцию для возврата итератора по умолчанию для объекта (перезаписав итератор по умолчанию) и посмотреть, как он вызывается:

const obj = {
    // Define a custom function for returning the default iterator for this object
    [Symbol.iterator]: function () {
        console.log('The iterator-returning function got invoked');
        // Return an iterator from some other object
        //  (but we could have created one from scratch as well):
        return 'abc'[Symbol.iterator]();
    },
    myMethod: function () {
        // This method happens to return the same iterator
        return this[Symbol.iterator]();
    },
    myOtherMethod: function () {
        // This method happens to return another iterator
        return 'def'[Symbol.iterator]();
    }
}

for (const a of obj) {
    console.log(a); // a b c
}
for (const a of obj.myMethod()) {
    console.log(a); // a b c
}
for (const a of obj.myOtherMethod()) {
    console.log(a); // d e f
}
.as-console-wrapper { max-height: 100% !important; top: 0; }

person trincot    schedule 10.10.2017