生成器
在函数名称前面加一个星号( * )表示这个函数是一个生成器。
注意: 箭头函数不能用来定义生成器函数
function* generator() {
...
} // 生成器函数
调用生成器函数会返回一个对象(通常叫做生成器对象)。注意:一开始生成器处于暂停执行的状态,此时生成器函数不会执行。
function* generator() {
console.log('打印内容?')
}
const gObj = generator()
console.log(gObj) // 结果见 generator-1.png
生成器对象实现了Iterator接口,因此具有next()方法。调用gObj的next()会返回一个有value属性和done属性的对象。同时生成器函数执行。
实现接口可懂? 类似
java的implements关键字。上面重点就是具有next()方法
const obj = gObj.next()
console.log(obj) // 结果见 generator-2.png
生成器对象也实现了Iterable接口。它们默认的迭代器是自引用的:
console.log(gObj === gObj[Symbol.iterator]()) // true
Iterable接口和Iterator接口???
实现了
Iterable接口意味着该对象定义了Symbol.Iterator方法。且这个方法必须返回实现了Iterator接口的对象实现了
Iterator接口意味着该对象定义了next()方法,且next()方法必须返回{done: Boolean, value: any}格式的对象对于生成器对象来说,其本身就实现了
next()方法,同时也实现了Symbol.iterator方法(返回的对象也定义了next()方法)
yield
yield可以让生成器对象调用next()方法来使生成器函数分阶段执行(保留上一次执行的状态)。
特性1:yield类似函数的中间返回语句(每个阶段的return)。
它返回的值会出现在生成器对象的next()方法返回的对象里(value)。
同时这个对象的done属性为false。
而通过return关键字退出的生成器函数会使生成器对象的done属性为true
不管是
yield还是return,返回的值都会成为next()返回的对象的value属性值
由于生成器对象实现了
iterable接口,所以其是一个**可迭代对象。**这意味着可通过yield来实现循环:function* generatorFunc() { yield 1 yield 2 yield 3 } for(let item of generatorFunc()) { console.log(item) } // 打印 1 2 3
特性2:yield关键字可以作为函数的中间参数使用。
什么意思呢?
就是指:上一次让生成器函数暂停的yield关键字会接收到传递给生成器对象的next()方法的第一个值**。也就是说 可以通过给生成器对象的next()方法传参来指定上一个yield表达式的值(注意了是整个表达式的值)
第一次调用
next()时,传入的值不会被使用。因为第一次调用是为了开始执行生成器函数,它没有上一个yield了。当遇到第一个
yield时,函数暂停执行,此时,这个yield就成为了第二次调用next()的上一个yield。
function* generatorFunc(initial) {
console.log(initial)
console.log(yield)
console.log(yield)
}
const gObj = generatorFunc('initialValue')
gObj.next('1st next')
gObj.next('2st next')
gObj.next('3st next')
// 结果为
// 'initialValue'
// '2st next'
// '3st next'
可以使用星号( * )来增强yield,让它能够迭代一个可迭代对象,从而一次产生(返回)一个值:
function* generatorFunc() {
yield* [1, 2, 3]
}
// 它们是等价的
function* generatorFunc() {
for(let item of [1, 2, 3]) {
yield item
}
}
需要注意的是:
yield*表达式的值是关联迭代器返回done: true时的value属性。那么:
对于普通迭代器来说,这个值就是
undefined。对于生成器函数产生的迭代器来说,这个值就是生成器函数的返回值。
因为生成器函数会返回一个实现了
Iterator接口的对象。而这对于可迭代对象的迭代器来说是相同的。所以,对于其他对象定义迭代器时,可以将[Symbol.iterator]换成* [Symbol.iterator]。然后借助yield可以轻而易举实现迭代const obj = { values: [1, 2, 3], * [Symbol.iterator]() { yield* this.values } } for(let item of obj) { console.log(item) } // 打印 1 2 3
扩展:
生成器对象除了一个必须有的next()方法外,还支持可选的return(),throw()
1、return()
调用return()方法会强制生成器进入关闭状态。也可以给return(arg)传递参数,参数的值会成为终止迭代器对象的值,也就是{ done: true, value: arg }
注意:只要通过return()进入关闭状态,就无法恢复了
for..of循环等内置语言结构会忽略状态
done: true的对象的value的值
2、throw()
...
示例
function * createIdMaker() {
let i = 1
while(true) {
yield i++
}
}
const idMaker = createIdMaker()
console.log(idMaker.next().value) // 1
console.log(idMaker.next().value) // 2
console.log(idMaker.next().value) // 3