问题描述
我的想法是,如果 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]
函数都会吐出一个迭代器,但是可以是旧迭代器,也可以是新迭代器?没有保证?
解决方法
您最初的看法是,迭代对象的 @@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 返回新的迭代器。