导入
可迭代对象与迭代器
JavaScript 中可迭代对象与迭代器及其行为简介
现在我们已经介绍了 for...of 循环和符号,可以轻松地讨论迭代器了。
了解迭代器是非常值得的,特别是如果你喜欢惰性求值,或者希望能够描述无限序列。理解迭代器也有助于你更好地掌握生成器、Promise、Set 和 Map。
在掌握了迭代器的基础知识后,我们将运用这些知识来理解生成器的工作原理。
ES6 引入了 可迭代协议。该协议定义了 JavaScript 对象的遍历行为。
一个 可迭代对象 拥有一个键为 Symbol.iterator 的迭代方法。该方法返回一个 迭代器对象。
1let iterableObject = { 2 [Symbol.iterator]() { return iteratorObject; } 3};
Symbol.iterator 是一个 公认的符号。接下来我们将使用 Symbol.iterator 来描述可迭代对象。请注意,这里使用这种写法只是为了帮助理解迭代器的工作机制。实际上,在你的代码中几乎不会直接用到 Symbol.iterator。很快你就会学到另一种定义可迭代对象的方法。
一个 迭代器对象 是一种具有 next 方法的数据结构。调用该方法时,它会返回下一个元素,并附带一个布尔值,用于指示是否已到达迭代的末尾。
1// 在 iterableObject 之前放置以下代码 2let iteratorObject = { 3 next() { 4 return { 5 done: true, 6 value: null 7 }; 8 } 9};
next 函数的返回值包含两个属性:
done被视为布尔值。当done为true时,表示迭代结束,此时value不再参与迭代。value是下一次迭代的值。只有当done为false时,value才会被视为迭代的一部分。当done为true时,value就成为迭代器的返回值。
让我们以一个倒计时对象为例:
1let countdownIterator = { 2 countdown: 10, 3 next() { 4 this.countdown -= 1; 5 return { 6 done: this.countdown === 0, 7 value: this.countdown 8 }; 9 } 10}; 11 12let countdownIterable = { 13 [Symbol.iterator]() { 14 return Object.assign({}, countdownIterator); 15 } 16}; 17 18let iterator = countdownIterable[Symbol.iterator](); 19 20console.log(iterator.next()); 21//> Object {done: false, value: 9} 22 23console.log(iterator.next()); 24//> Object {done: false, value: 8}
注意,迭代的状态被保留了下来。
Object.assign 的作用是:每次可迭代对象返回迭代器时,都会创建一个迭代器对象的浅拷贝。这使得同一个可迭代对象可以拥有多个独立的迭代器,每个迭代器都保存自己的内部状态。如果没有 Object.assign,那么多个迭代器将只是指向同一个迭代器对象。考虑下面这段代码:
1let secondIterator = countdownIterable[Symbol.iterator](); 2let thirdIterator = countdownIterable[Symbol.iterator](); 3 4console.log(secondIterator.next()); 5//> Object {done: false, value: 9} 6 7console.log(thirdIterator.next()); 8//> Object {done: false, value: 9} 9 10console.log(secondIterator.next()); 11//> Object {done: false, value: 8}
现在我们来学习如何使用迭代器和可迭代对象。