How does `for..of` loop resolve the iterator from an object?

喜欢而已 提交于 2019-12-01 19:45:34

The specific place where the operation is specified is in 7.4.1 GetIterator( obj [ , method ] ). This gets the @@iterator property of the passed object in step 1a. of the abstract operation:

a. Set method to GetMethod(obj, @@iterator).

@@iterator is a well-known symbol that is the Symbol.iterator property on objects.

This is used by for-in and for-of loops due to the productions in 13.7.5.11 Runtime Semantics:

IterationStatement : for(ForDeclaration of AssignmentExpression) Statement

  1. Let keyResult be the result of performing ForIn/OfHeadEvaluation(BoundNames of ForDeclaration, AssignmentExpression, iterate).
  2. Return ForIn/OfBodyEvaluation(ForDeclaration, Statement, keyResult, iterate, lexicalBinding, labelSet).

Here, you can see the iterator argument passed to ForIn/OfBodyEvaluation is the return value keyResult of ForIn/OfHeadEvaluation. The return value is, in step 7b:

b. Return GetIterator(exprValue).

Thus, for-of loops get the iterator by accessing the @@iterator or Symbol.iterator well-known symbol by specification.

An object can define only one symbol Symbol.iterator, which is the one that will be called on iteration on the object itself. The other properties of the object, such as the examples you have given (entries, keys, values) may also return an iterator, but those are in general not the same iterators. They could be the same, but that is just an implementation choice. There is no ambiguity as to which iterator is called when iterating the object with for..of. It is the one returned by [Symbol.iterator].

  1. How iterator is obtained from an object?

You can get it by calling the function keyed with Symbol.iterator, e.g.

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

It is retrieved implicitly with for..of.

  1. Where is it specified in the spec?

This table in the specs explains:

@@iterator "Symbol.iterator"

A method that returns the default Iterator for an object. Called by the semantics of the for-of statement.

Here is how you can make a custom function for returning the default iterator for an object (overwriting the default one), and see how it gets called:

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; }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!