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

4

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

我好奇的原因是,例如,Map 定义了带有多个迭代器(entries、values、keys)的接口,如果没有明确指定,则似乎 for..of 循环使用由 map.entries() 调用返回的迭代器。

我尝试在规范中搜索,但它只指定将 iterator 作为参数传递给抽象操作 ForOf

使用参数 lhs、stmt、iterator、iterationKind、lhsKind 和 labelSet 调用抽象操作 ForIn/OfBodyEvaluation。

所以基本上有两个问题:

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

1
请检查Forin/OfHeadEvaluation的最后一步。 - Bergi
1
如果没有明确指定,for..of循环将使用由map.entries()调用返回的迭代器。- 不完全正确。它始终使用map[Symbol.iterator]()虽然这是与entries相同的方法。如果您传递一个迭代器对象,它也会在其上调用… [Symbol.iterator](), 只是该方法在迭代器实例上返回对象本身(return this;)。 - Bergi
@Bergi,非常感谢您的确认,这正是我所想的。 - Max Koretskyi
2个回答

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

a. 将method设置为GetMethod(obj, @@iterator)。

@@iterator是一个众所周知的符号,它是对象上的Symbol.iterator属性。
由于13.7.5.11运行时语义学中的产生式,它被for-in和for-of循环使用。

IterationStatement : for(ForDeclaration of AssignmentExpression) Statement

  1. keyResult成为执行ForIn/OfHeadEvaluation(绑定名称为ForDeclaration的变量,AssignmentExpression,迭代器)的结果。
  2. 返回ForIn/OfBodyEvaluation(ForDeclarationStatementkeyResult,迭代器,词法绑定,labelSet)。
在这里,您可以看到传递给ForIn/OfBodyEvaluation的迭代器参数是ForIn/OfHeadEvaluation的返回值keyResult。 在步骤7b中,返回值为:

b. 返回GetIterator(exprValue)。

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

非常感谢,这正是我在寻找的内容。GetIterator ( obj [ , method ] ) 可以选择性地接受一个方法,从 7b 中可以看出该方法未被指定,因此 Set method to ? GetMethod(obj, @@iterator). 是正确的吗?同时我想知道在 GetMethod 前面的问号是什么意思。你有什么想法吗? - Max Koretskyi
@AngularInDepth.com 是的,只有 exprValue 被传递。 - Andrew Li
@AngularInDepth.com 这里有详细描述:https://dev59.com/uJ7ha4cB1Zd3GeqPh1ee - Andrew Li

1
一个对象只能定义一个符号Symbol.iterator,这是在对象自身迭代时将调用的符号。对象的其他属性,例如您提供的示例(entrieskeysvalues),也可能返回迭代器,但通常不是同一迭代器。它们可以是相同的,但这只是一种实现选择。在使用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; }


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接