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

时间:2017-10-10 11:26:12

标签: javascript iterator ecmascript-2017 for-of-loop

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

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

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

  

使用参数调用抽象操作ForIn / OfBodyEvaluation   lhs,stmt,迭代器,iterationKind,lhsKind和labelSet。

所以基本上有两个问题:

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

2 个答案:

答案 0 :(得分:1)

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

  

一个。将方法设置为GetMethod obj ,@@ iterator)。

@@iteratorwell-known symbol,是对象的Symbol.iterator属性。

由于13.7.5.11 Runtime Semantics中的制作而由for-in和for-for循环使用:

  

IterationStatement :for(&em> AssignmentExpression 的 ForDeclaration 语句

     
      
  1. keyResult 成为执行ForIn/OfHeadEvaluation ForDeclaration 的绑定名称, AssignmentExpression ,迭代)的结果。
  2.   
  3. 返回ForIn/OfBodyEvaluation ForDeclaration Statement keyResult ,iterate,lexicalBinding, labelSet
  4.   

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

  

湾返回GetIterator exprValue )。

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

答案 1 :(得分:1)

对象只能定义一个符号Symbol.iterator,这是在对象本身上迭代时调用的符号。 对象的其他属性(例如您给出的示例(entrieskeysvalues)也可能返回迭代器,但这些迭代器通常不是相同的迭代器。他们可以相同,但这只是一个实现选择。在使用for..of迭代对象时,调用迭代器没有任何歧义。这是[Symbol.iterator]返回的那个。

  
      
  1. 如何从对象获取迭代器?
  2.   

您可以通过调用Symbol.iterator键入的功能来获取它,例如

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

使用for..of隐式检索它。

  
      
  1. 规范中指定了哪里?
  2.   

This table in the specs解释说:

  

@@ iterator" Symbol.iterator"

     

返回对象的默认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; }