在 JavaScript ES6 中,每次调用 obj[Symbol.iterator] 函数时,iterable 是否会吐出一个新的迭代器?

问题描述

我的想法是,如果 obj 是可迭代的,那么每次调用 obj[Symbol.iterator] 函数时,它都会吐出一个全新的迭代器。现在,aMap.entries() 返回一个可迭代对象,因此:

map = new Map([["a",3],["b",'hi'],["c",true]]);

for (const e of map.entries()) {
    console.log(e);
}

for (const e of map.entries()) {
    console.log(e);
}

const someEntries = map.entries();

console.log(someEntries.next());

console.log(someEntries[Symbol.iterator]);

console.log(someEntries[Symbol.iterator]().next());

console.log(someEntries[Symbol.iterator]().next());

console.log(someEntries[Symbol.iterator]().next());

console.log(someEntries[Symbol.iterator]().next());

这就是为什么上面的 for-of 循环可以打印出所有键值对两次。

然而,代码的最后 6 行,当我调用函数并使用 next() 时,它给出了 next() 函数的“延续”......所以它不是一个全新的迭代器,但旧的。因此,这与“每次调用 obj[Symbol.iterator] 函数时,都会输出一个全新的迭代器”的想法相冲突。

那是什么意思?每次调用obj[Symbol.iterator]函数都会吐出一个迭代器,但是可以是旧迭代器,也可以是新迭代器?没有保证?

例如下面的代码显示它每次都吐出一个新的迭代器:

const arr = [1,3,5];

console.log(arr[Symbol.iterator]().next());

console.log(arr[Symbol.iterator]().next());

console.log(arr[Symbol.iterator]().next());

解决方法

您最初的看法是,迭代对象的 @@iterator 方法每次调用时都应该产生一个新的迭代器,是正确的

你对不同的现象感到困惑。

当您调用地图的 @@iterator 方法(或 Map#entries,两者都执行相同的操作)时,您将获得一个全新的迭代器,您可以显式使用该迭代器。

如果您在地图上使用 for..of,它将在内部调用 @@iterator 方法,并执行相同的操作。

map = new Map([["a",3],["b",'hi'],["c",true]]);

//This will use @@iterator:
for (const e of map) {
    console.log(e);
}

//Or do the same manually:
const someEntries = map.entries();

console.log(someEntries.next().value);
console.log(someEntries.next().value);
console.log(someEntries.next().value);

但是,有些情况下您想将两者混合使用。例如:

map = new Map([["a",true]]);

const someEntries = map.entries();

//Get the first
console.log('The first element is:',someEntries.next().value);

//Consume the rest
for (const e of someEntries) {
    console.log(e);
}

然而,要做到这一点,我们必须将我们的迭代器传递给 for..of(如果我们传递了我们的地图,它将创建一个新的迭代器,然后重新开始)。它将(一如既往)尝试调用其 @@iterator 方法(将没有 @@iterator 方法的迭代器传递给 for..of 将导致错误)。因此,要实现这一点,所有原生迭代器都必须有自己的 @@iterator 方法,也就是说,大多数情况下返回迭代器对象本身,这就是 迭代器 em> 可由原生结构迭代。

因此,当您在迭代器上调用 @@iterator 时,它不会重新开始,而是进一步消耗自身。

这个片段可能会更清楚:

map = new Map([["a",true]]);

const someEntries = map.entries();

console.log(someEntries[Symbol.iterator] === map[Symbol.iterator]) //false,the iterator doesn't share its @@iterator method with the map
console.log(someEntries[Symbol.iterator]() === someEntries) //true,calling @@iterator on an iterator returns itself

const stillSomeEntries = someEntries[Symbol.iterator]() //The same iterator as someEntries

const someOtherEntries = map.entries();

console.log(someEntries.next().value);
console.log(stillSomeEntries.next().value);
console.log(someEntries.next().value);

console.log(someOtherEntries.next().value);
console.log(someOtherEntries.next().value);
console.log(someOtherEntries.next().value);

,

那是什么意思?没有保证?

是的。大多数迭代器确实会创建一个新的迭代器对象,但不是全部。迭代器本身通常是可迭代的(因此您可以编写 const entriesIter = map.entries(); for (const e of entriesIter) …),并且它们通常每次都返回相同的迭代器 - 它们本身:entriesIter[Symbol.iterator]() === entriesIter

然而,map.entries()map[Symbol.iterator]()arr[Symbol.iterator]() do 返回新的迭代器。