01-迭代器和生成器
第12章 迭代器和生成器
ES6引入了两个非常重要的概念:迭代器和生成器。由于生成器依赖于迭代器,所以本章将从迭代器开始。
迭代器可以粗略地比做书签:它可以帮助用户追踪当前的位置。数组就是一个可迭代对象的例子:它包含多个东西(比如,一本书的书页),同时提供一个迭代器(就像书签一样)。可以把这个比喻再具体化一些:想象有一个叫作book的数组,其中的每个元素都是一个代表书页的字符串。为了与本书的格式保持一致,这里使用路易斯· 卡罗尔所著《爱丽丝梦游仙境》中的“一闪一闪小蝙蝠”的歌词为例。(可以把它想象成那种每页只有一句话的儿童读物。)
const book = [
"Twinkle, twinkle, little bat!",
"How I wonder what you're at!",
"Up above the world you fly,",
"Like a tea tray in the sky.",
"Twinkle, twinkle, little bat!",
"How I wonder what you're at!",
];
有了book数组,就可以通过数组的values方法获取迭代器:
const it = book.values();
继续图书的例子,迭代器(通常很短小)是一个书签,不过它只适用于这本特定的书。此外,我们还没有在别的地方使用它,也没有开始阅读。在“开始阅读”时,调用迭代器的next方法, next 返回的对象中有两个属性: value (保存当前“页”)和 done ,在阅读过一页后, done 的值就变成 false 。由于书只有6页,所以很容易模拟阅读的过程:
it.next(); // { value: "Twinkle, twinkle, little bat!", done: false }
it.next(); // { value: "How I wonder what you're at!", done: false }
it.next(); // { value: "Up above the world you fly,", done: false }
it.next(); // { value: "Like a tea tray in the sky.", done: false }
it.next(); // { value: "Twinkle, twinkle, little bat!", done: false }
it.next(); // { value: "How I wonder what you're at!", done: false }
it.next(); // { value: undefined, done: true }
it.next(); // { value: undefined, done: true }
it.next(); // { value: undefined, done: true }
这里有几件重要的事情要说明一下。首先,当next方法返回本书的最后一页时,它需要说明阅读尚未结束。这也是用书来做比喻的不恰当之处:当读完一本书的最后一页时,才算是读完了。而迭代器的使用范围远远超过书本,想知道什么时候结束并不是那么容易。运行结束的时候,需要注意此时value的值是undefined,仍然可以继续调用next,只不过它的返回值都是一样的。当迭代过程结束的时候,才算真正结束,此时它就不能再提供数据了[1]。
虽然这个例子不是那么直接,不过大家也应该已经发现了,可以在两次 it.next() 调用之间做别的事情。也就是说,它已经预留了操作空间。
如果需要枚举数组,可以使用 for 循环,或者 for…of 循环。 for 循环的结构很简单:因为数组中的元素都是可数并且有序的,所以可以用 index 变量依次获取数组中的每个元素。那么 for…of 循环呢?它是如何在没有 index 的情况下完成这个魔法般的过程的?事实证明,它使用了迭代器: for…of 循环对任何可以提供迭代器的结构都适用。接下来大家马上就会知道如何利用这一点。首先,来看看如何在while循环中用上述的新发现(迭代器)来模拟一个 for…of 循环:
const it = book.values();
let current = it.next();
while(!current.done) {
console.log(current.value);
current = it.next();
}
注意,迭代器是不一样的,也就是说,每当创建一个新的迭代器时,就会从头开始,这样就可以在不同的地方使用多个迭代器了:
const it1 = book.values();
const it2 = book.values();
// 两个迭代器都未开始
// 通过it2读取两页:
it1.next(); // { value: "Twinkle, twinkle, little bat!", done: false }
it1.next(); // { value: "How I wonder what you're at!", done: false }
// 通过it2读取一页:
it2.next(); // { value: "Twinkle, twinkle, little bat!", done: false }
// 通过it1读取另一页:
it1.next(); // { value: "Up above the world you fly,", done: false }
在本例中,两个迭代器是互相独立的,它们可以分别依照各自的安排进行遍历。