`for..of` 循环如何从对象中解析迭代器?

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

对于实现可迭代接口的对象,它必须实现指向 returns iterator 函数的 [Symbol.iterator] 键。我想知道 for..of 循环是否在内部调用对象上的此方法来获取 iterator

我很好奇的原因是,例如,Map 定义了一个带有多个迭代器(条目、值、键)的接口,而且如果没有明确指定,for..of 循环似乎使用 map.entries() 调用返回的迭代器。

我正在尝试搜索 in the specification 但它只指定 iterator 作为参数传递给抽象操作 ForOf:

The abstract operation ForIn/OfBodyEvaluation is called with arguments lhs, stmt, iterator, iterationKind, lhsKind, and labelSet.

所以基本上有两个问题:

  1. 如何从对象中获取迭代器?
  2. 规范中哪里规定的?

具体操作指定的地方在7.4.1 GetIterator( obj [ , method ] )。这将获取步骤 1a 中传递的对象的 @@iterator 属性。抽象操作:

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

@@iterator 是一个 well-known symbol 即对象上的 Symbol.iterator 属性。

由于 13.7.5.11 Runtime Semantics 中的产生式,for-in 和 for-of 循​​环使用了它:

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).

在这里,您可以看到传递给 ForIn/OfBodyEvaluation is the return value keyResult of ForIn/OfHeadEvaluation 的迭代器参数。 return 值是,在步骤 7b 中:

b. Return GetIterator(exprValue).

因此,for-of 循​​环通过按规范访问 @@iteratorSymbol.iterator 众所周知的符号来获取迭代器。

一个对象只能定义一个符号Symbol.iterator,即在对象自身上迭代时调用的符号。 对象的其他属性,比如你给出的例子(entrieskeysvalues)也可能是return一个迭代器,但这些一般不是相同的迭代器。它们 可以 相同,但这只是一种实现选择。在用 for..of 迭代对象时,调用哪个迭代器是没有歧义的。它是由 [Symbol.iterator] 编辑的 return。

  1. How iterator is obtained from an object?

您可以通过调用Symbol.iterator键控的函数来获取它,例如

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

它是用 for..of 隐式检索的。

  1. Where is it specified in the spec?

This table in the specs 说明:

@@iterator "Symbol.iterator"

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

下面是如何为对象的默认迭代器 return 创建自定义函数(覆盖默认迭代器),并查看它是如何被调用的:

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; }