展开运算符如何从 Javascript 中带有 Iterator/Generator 的对象中获取值?

How does spread operator get values from an object with an Iterator/Generator in Javascript?

我有以下一段代码定义了一个数组,然后一个带有生成器的迭代器对象从这个数组中产生值,我用扩展运算符输出每个值:

const arr = ['0', '1', '4', 'a', '9'];
const my_obj = {
  [Symbol.iterator]: function*() {
    for(let index of arr) {
      yield `${index}`;
    }
  }
};

const all = [...my_obj] 

console.log(...my_obj)

结果是:

0
1
4
a
9

我不明白的是,如果“my_obj”是对象而不是数组,展开运算符变量“...my_obj”如何获取数组的值。据我了解:“my_obj”正在接收一个对象,如果您应用扩展运算符,它应该得到“key:value”。

谁能解释一下它是如何获得这些值的?

扩展运算符允许在需要零个或多个参数(用于函数调用)或元素(用于数组文字)或对象表达式的地方扩展数组表达式或字符串等可迭代对象在需要零个或多个 key-value 对(对于对象文字)的地方。

我们可以通过自己制作一个来轻松掌握迭代器的概念。

例如,我们有一个不是数组的对象,但看起来适合 for..of.

  1. 当 for..of 启动时,它会调用该方法一次(如果未找到则出错)。该方法必须 return 一个迭代器——一个带有 next 方法的对象。
  2. 以后,for..of 仅适用于 returned 对象。
  3. 当 for..of 需要下一个值时,它会对该对象调用 next()。
  4. next()的结果必须是{done: Boolean, value: any}形式,其中done=true表示迭代结束,否则value为下一个值。

如果对象满足for可迭代,即for..of循环。它可以使用扩展运算符进行扩展,并且可以在数组中分配,因为数组也是一个可迭代的。

参考: iterables in javascript spread operator

扩展运算符和for...of语句调用对象的iterable protocol。一些对象,如 ArrayStringSetMap 已经内置了可迭代协议。这意味着他们有 @@iterator 方法。

您自己刚刚创建了一个对象并给了它 属性 的 [Symbol.iterator]。现在你的对象知道在这个对象上调用 for...of 的扩展语法时要做什么,也就是调用这个迭代器并循环遍历由 [Symbol.iterator] 键中的生成器函数创建的可迭代对象。

并且在您的生成器函数中,您已指定在每次迭代中产生 arr 的值,直到该循环完成。

MDN 显示了一个示例 here,它说:

Some built-in constructs—such as the spread syntax—use the same iteration protocol under the hood: console.log([...someString]); // ["h", "i"]

您有时会看到的一种模式是对象具有 values 方法。大多数时候这实际上是一个生成器函数。当调用 [Symbol.iterator] 时,它 returns 生成器函数然后循环对象中的值。

我在这里创建了一个小演示,它接受一个字符串并使用 for...of 遍历字母并使用 spread 运算符将它们排列。 [Symbol.iterator] 将调用一个生成器函数来查找每个字母在字母表中的位置。两者都使用相同的可迭代协议。

class LettersObjects {

  *values() {
    // Here we use the iterable protocol of a string.
    for (const letter of this.letters) {
      const position = this.alphabet.indexOf(letter.toLowerCase()) + 1;
      yield position.toString().padStart(2, '0');
    }
  }

  // This is called with for...of and ...spread.
  [Symbol.iterator]() {
    return this.values();
  }
  
  constructor(letters) {
    this.letters = letters;
    this.alphabet = 'abcdefghijklmnopqrstuvwxyz';
  }
  
}

const letters = new LettersObjects('Hello world');

// Call the iterable protocol on our LetterObjects instance.
for (const letter of letters) {
  console.log('loop:', letter);
}

// Call the iterable protocol on our LetterObjects instance.
console.log('Spread:', ...letters);

发生这种情况是因为对象是可迭代对象。

传播运算符传播可迭代对象,而不是专门传播数组。它可以传播对象,甚至是字符串。

Spread operator documentation