关注[前端小讴],阅读更多原创技术文章
相关代码 →
*
function* generatorFn() {
} // 生成器函数声明
let gfn = function* () {
} // 生成器函数表达式
let foo = {
*generatorFn() {
}, // 生成器函数作为对象字面量方法
}
class Foo {
*generatorFn() {
} // 生成器函数作为类实例方法
}
class FooStatic {
static *generatorFn() {
} // 生成器函数作为类静态方法
}
next()
方法const g = generatorFn() // 调用生成器函数,产生生成器对象
console.log(g) // generatorFn {},生成器对象
console.log(g.next) // 生成器对象具有next()方法
next()
方法的返回值类似于迭代器,有done 属性和value 属性next()
就会达到done:true
状态console.log(g.next()) // { value: undefined, done: true },函数体为空
value
的返回值(默认为 undefined)function* generatorFn2() {
return 'foo'
}
const g2 = generatorFn2()
console.log(g2.next()) // { value: 'foo', done: true }
console.log(g2.next()) // { value: undefined, done: true },耗尽
next()
方法后开始执行function* generatorFn3() {
console.log('生成器函数开始执行')
}
const g3 = generatorFn3() // 调用生成器函数,产生生成器对象(生成器函数还未执行,不打印日志)
g3.next() // '生成器函数开始执行',初次调用next()方法,生成器函数开始执行,打印日志
function* generatorFn4() {
}
console.log(generatorFn4) // ƒ* generatorFn4() {},生成器函数
const g4 = generatorFn4()
console.log(g4) // generatorFn4 {},生成器对象
console.log(g4[Symbol.iterator]) // ƒ [Symbol.iterator]() { [native code] },迭代器工厂函数
console.log(g4[Symbol.iterator]()) // generatorFn4 {},迭代器
console.log(g4 === g4[Symbol.iterator]()) // true,生成器对象的默认迭代器是自引用的
yield
关键字可以让生成器停止和开始执行:
yield
关键字之前正常执行yield
关键字后停止执行,函数作用域的状态被保留next()
方法恢复执行function* generatorFn5() {
yield
}
let g5 = generatorFn5()
console.log(g5.next()) // { value: undefined, done: false },yield生成的值
console.log(g5.next()) // { value: undefined, done: true },恢复执行生成的值
yield
关键字与函数的return
语句作用相似,其生成的值会出现在next()
方法返回的对象里,但done
的状态不同:
yield
关键字退出的生成器函数会处在done:false
状态return
关键字退出的生成器函数会处在done:true
状态function* generatorFn6() {
yield 'foo'
yield 'bar'
return 'baz'
}
let g6 = generatorFn6()
console.log(g6.next()) // { value: 'foo', done: false },yield关键字退出
console.log(g6.next()) // { value: 'bar', done: false },yield关键字退出
console.log(g6.next()) // { value: 'baz', done: true },return关键字退出
next()
方法不影响其他生成器let g7 = generatorFn6() // 生成器对象g7
let g8 = generatorFn6() // 生成器对象g8
console.log(g7.next()) // { value: 'foo', done: false }
console.log(g8.next()) // { value: 'foo', done: false }
console.log(g8.next()) // { value: 'bar', done: false }
console.log(g7.next()) // { value: 'bar', done: false }
yield
关键字必须在生成器函数内部,直接位于生成器函数定义中使用,用在其他地方或嵌套的非生成器函数会报错function* validGeneratorFn() {
yield 'result'
}
function* invalidGeneratorFnA() {
function a() {
yield 'result' // SyntaxError: Unexpected string
}
}
function* invalidGeneratorFnB() {
const b = () => {
yield 'result' // SyntaxError: Unexpected string
}
}
function* invalidGeneratorFnC() {
;(() => {
yield 'result' // SyntaxError: Unexpected string
})()
}
function* generatorFn7() {
// 生成器函数
yield 1
yield 2
yield 3
}
for (const x of generatorFn7()) {
// 调用生成器函数generatorFn7,generatorFn7()是生成器对象
console.log(x)
/*
1
2
3
*/
}
function* nTimes(n) {
while (n--) {
console.log(n)
yield
}
}
for (let _ of nTimes(3)) {
console.log(_)
/*
2,第1次循环n
undefined,第1次循环yield
1,第2次循环n
undefined,第2次循环yield
0,第3次循环n
undefined,第3次循环yield
*/
}
yield
关键字还可以作为函数的中间参数使用
yield
关键字会接收到传给next()
方法的第一个值next()
传入的值不会被使用,因为仅仅是为了开始执行生成器函数function* generatorFn8() {
console.log(yield)
console.log(yield)
console.log(yield)
}
let g9 = generatorFn8() // 调用生成器函数,产生生成器对象
g9.next('bar') // 第一次调用next()的值不会被使用,仅作为开始执行生成器函数
g9.next('baz') // 'baz',调用next()传入baz,参数作为交给同一个yield的值
g9.next('qux') // 'qux',调用next()传入qux,参数作为交给同一个yield的值
yield
关键字同时用于输入和输出(与return
关键字同时使用)
next()
方法没有参数,生成器函数直到遇到yield
关键字停止执行next()
方法有参数,参数作为交给同一个 yield 的值,生成器函数执行return
返回本次传入的值function* generatorFn9() {
return yield 'foo'
}
let g10 = generatorFn9()
console.log(g10.next()) // { value: 'foo', done: false },next()没有参数,遇到yield关键字暂停执行,并计算要产生的值
console.log(g10.next('bar')) // { value: 'bar', done: true },next()有参数,参数作为交给同一个yield的值,相当于return 'bar'
yield
关键字多次使用function* generatorFn10() {
for (let i = 0; ; i++) {
yield i
}
}
let g11 = generatorFn10()
console.log(g11.next()) // { value: 0, done: false }
console.log(g11.next()) // { value: 1, done: false }
console.log(g11.next()) // { value: 2, done: false }
console.log(g11.next()) // { value: 3, done: false }
function* nTimes(n) {
let i = 0
while (n--) {
yield i++
}
}
for (let x of nTimes(3)) {
console.log(x)
/*
0
1
2
*/
}
function* range(start, end) {
while (end > start) {
yield start++
}
}
for (const x of range(4, 7)) {
console.log(x)
/*
4
5
6
*/
}
function* zeros(n) {
while (n--) {
yield 0
}
}
console.log(zeros(8)) // zeros {},生成器对象
console.log(Array.from(zeros(8))) // [0, 0, 0, 0, 0, 0, 0, 0],生成器对象作为可迭代对象
function* fibonacci() {
let arr = [0, 1]
let [prev, curr] = arr
while (true) {
;[prev, curr] = [curr, prev + curr]
arr.push(curr)
yield arr
}
}
function Fibonacci(n) {
if (n === 1) {
// 第1项
return 0
} else if (n === 2 || n === 3) {
// 第2、3项
return 1
} else {
// 第4项或之后
let num = 0
const fibo = fibonacci()
for (let i = 3; i <= n; i++) {
num = fibo.next().value
}
return num
}
}
console.log(Fibonacci(8).join()) // 0,1,1,2,3,5,8,13
*
增强yield
,让其能够迭代一个可迭代对象,yield*
将一个可迭代对象序列化为一连串单独产出的值function* generatorFn11() {
yield* [1, 2, 3]
}
let g12 = generatorFn11()
for (const x of generatorFn11()) {
console.log(x)
/*
1
2
3
*/
}
// 等价于
function* generatorFn11() {
for (const x of [1, 2, 3]) {
yield x
}
}
yield*
的值是**关联迭代器返回done:true
**时value
的属性:
done:true
代表迭代器耗尽,这个值是 undefined
function* generatorFn12() {
console.log('iterValue', yield* [1, 2, 3])
}
for (const x of generatorFn12()) {
console.log('value', x)
/*
value 1
value 2
value 3
iterValue undefined
*/
}
done:true
的值是return 返回的值(没有 return 值则返回 undefined)function* innerGeneratorFn() {
yield 'foo'
return 'bar'
}
function* outerGeneratorFn() {
console.log('iterValue', yield* innerGeneratorFn())
}
for (const x of outerGeneratorFn()) {
console.log('value', x)
/*
value foo
iterValue bar
*/
}
yield*
实现递归算法yield*
实现递归,此时生成器可以产生自身function* nTimes(n) {
if (n > 0) {
yield* nTimes(n - 1) // 生成器对象作为可迭代对象
yield n
}
}
for (const x of nTimes(3)) {
console.log(x)
/*
1
2
3
*/
}
Iterable
接口,生成器函数和默认迭代器被调用之后都产生迭代器class Foo2 {
// Foo既是迭代器,又是生成器函数
constructor() {
this.values = [1, 2, 3]
}
*[Symbol.iterator]() {
yield* this.values
}
}
const f = new Foo2() // 产生可迭代的生成器对象
for (const x of f) {
console.log(x)
/*
1
2
3
*/
}
Iterator
接口的对象一定有next()
方法,还有一个可选的return()
方法,生成器还有第三个方法throw()
return()
和throw()
都可以用于强制生成器进入关闭状态function* generatorFn13() {
}
let g13 = generatorFn13() // 生成器对象
console.log(g13.next) // ƒ next() { [native code] }
console.log(g13.return) // ƒ return() { [native code] }
console.log(g13.throw) // ƒ throw() { [native code] }
return()
方法返回种种迭代器对象的值(即 return()方法的参数)
function* generatorFn14() {
yield* [1, 2, 3]
}
let g14 = generatorFn14()
console.log(g14) // generatorFn14 {}
console.log(g14.return(5)) // {value: 5, done: true}
console.log(g14) // generatorFn14 {}
return()
方法进入关闭状态的生成器对象,后续调用next()
都会显示done:true
状态,后续提供的任何返回值都不再被存储或传播console.log(g14.next()) // { value: undefined, done: true },已经调用过return()
console.log(g14.next()) // { value: undefined, done: true }
console.log(g14.next()) // { value: undefined, done: true }
for-of
等内置语言结构会忽略状态为done:true
的迭代器对象内部返回的值(忽略 undefined)let g15 = generatorFn14()
for (const x of g15) {
x > 1 && g15.return() // x大于1则停止生成器
console.log(x)
/*
1
2
自动忽略done:true返回的value(undefined)
*/
}
throw()
方法会在暂停的时候,将一个提供的错误注入到生成器对象中
function* generatorFn15() {
yield* [1, 2, 3]
}
let g16 = generatorFn15()
console.log(g16) // generatorFn15 {}
try {
g16.throw('foo') // 注入错误
} catch (err) {
console.log(err) // 'foo'
}
console.log(g16) // generatorFn15 {},错误未被处理,生成器关闭
yield
function* generatorFn16() {
for (const x of [1, 2, 3]) {
// 错误在生成器的try/catch块中抛出 -> (生成器对象已开始执行)在生成器内部被捕获
// 若生成器对象未开始执行,则throw()抛出的错误不会在生成器内部被捕获
try {
yield x // 在yield关键字处暂停执行
} catch (err) {
console.log(err) // 'foo'
}
}
}
let g17 = generatorFn16()
console.log(g17.next()) // { value: 1, done: false }
g17.throw('foo') // 注入错误
console.log(g17.next()) // { value: 3, done: false },跳过对应的yield
yield*
的作用是什么?在普通迭代器、生成器函数产生的迭代器中,yield*
的值分别是什么?