如何理解迭代器(Iterator)和生成器(Generator)

所有的新概念并不是人们闲的没事想出来的,而是为了解决已有的某些问题才出现的。所以只要我们从历史的角度去看,就能更容易理解这些新概念!

什么是迭代器

我们在用循环语句迭代数据时,必须要初始化一个变量来记录每次迭代在数据集中的位置,比如说for循环:

1
2
3
4
const arr = ['red', 'green', 'blue']
for (let index = 0; index < arr.length; index++) {
console.log(i)
}

在上面的代码中,我们就是通过变量i来跟踪索引,我们通过观察i来了解现在循环到数组中的第几个项了。

这个例子中的循环很简单,但是如果嵌套循环,则需要追踪多个变量,代码复杂度增加,就很容易出错。就像Promise的出现是为了解决回调地域一样,迭代器的出现就是为了消除这种复杂性并减少循环的错误。

为了更好的理解迭代器的运行方式,我们先用 ES5 的语法写一个迭代器函数,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function createIterator(items) {
var i = 0
return {
next: function() {
var done = i >= items.length
var value = !done ? items[i++] : undefined
return {
done,
value
}
}
}
}

var iterator = createIterator([1, 2, 3])
console.log(iterator.next()) // {value:1, done:false}
console.log(iterator.next()) // {value:2, done:false}
console.log(iterator.next()) // {value:3, done:false}
console.log(iterator.next()) // {value:undefined, done:true}

其实这段代码已经给出了迭代器的定义:迭代器函数定义了一个next()方法,这个方法执行后会返回一个对象,拥有valuedone两个属性,value告诉你下次执行时候的返回值,done告诉你是否执行完了。

什么是生成器

生成器就是“能够返回一个迭代器函数的函数”。

上面的迭代器我们是自己定义的,比较麻烦,所以官方出了一个可以生成迭代器的函数,试着比较一下下面两段效果一样的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 自定义迭代器
function createIterator(items) {
var i = 0
return {
next: function() {
var done = i >= items.length
var value = !done ? items[i++] : undefined
return {
done,
value
}
}
}
}

// 使用生成器函数返回迭代器
function* createIterator(items) {
for (let i = 0; i < items.length; i++) {
yield items[i]
}
}

就是这么简洁好用!

还是上面那个例子,我们粘过来,不过这一次我们使用生成器函数:

1
2
3
4
5
6
7
8
9
10
11
function* createIterator(items) {
for (let i = 0; i < items.length; i++) {
yield items[i]
}
}
let iterator = createIterator([1, 2, 3])

console.log(iterator.next()) // { value:1, done: false }
console.log(iterator.next()) // { value:2, done: false }
console.log(iterator.next()) // { value:3, done: false }
console.log(iterator.next()) // { value:undefined, done: true }

其中,function*用来声明一个生成器,yield是一个关键字。

yield是啥?在调用迭代器的next()方法时,遇到yield就会暂停,然后返回迭代器的结果对象{ value: xx, done: xx }。当我们再次调用生成器的next()方法时,如果它还没执行到头(done 不为 true),那么他就会继续执行。

以上。