前言
生成器gengrator是es6 新增的函数功能,它允许你定义一个包含自有迭代算法的函数, 同时它可以自动维护自己的状态。 本文来总结一下JavaScript 中生成器的相关知识点。
正文
1、 生成器是什么
生成器函数提供了一个强大的选择:它允许你定义一个包含自有迭代算法的函数, 同时它可以自动维护自己的状态。 生成器函数使用 function*
语法编写。 最初调用时,生成器函数不执行任何代码,而是返回一种称为Generator的迭代器。 通过调用生成器的下一个方法消耗值时,Generator函数将执行,直到遇到yield关键字。
2、 生成器实例( 通过yield 中断执行)
(1)普通生成器函数
调用生成器函数会产生一个类似于迭代器的生成器对象,。生成器对象一开始处于暂停执行(suspended)的状态。与迭代器相似,生成器对象也实现了 Iterator 接口,它们默认的迭代器是自引用的,因此具有 next() 方法。调用这个方法会让生成器开始执行,遇到yield关键字函数暂停,再次调用next()继续执行函数,yield并不回像return 一样立即结束函数,只是暂停这个生成器函数。
注意:箭头函数不能用来定义生成器函数
function* createIterator() { yield 1; yield 2; yield 3; } console.log(createIterator());// createIterator {<suspended>} console.log(createIterator()[Symbol.iterator]());// createIterator {<suspended>},因此可以通过生成器创建迭代器函数 let iterator = createIterator() console.log(iterator.next().value);//1 console.log(iterator.next().value);//2 console.log(iterator.next().value);//3
(2)函数表达式的生成器函数,使用函数表达式来创建一个生成器
let myIterator = function* (items) { for (let i = 0; i < items.length; i++) { yield items[i] } } let myIterator1 = myIterator([1, 2, 3]) console.log(myIterator1.next().value);//1 console.log(myIterator1.next().value);//2 console.log(myIterator1.next().value);//3
(3)对象类型的生成器函数
var obj = { createIterator: function* (item) { for (let i = 0; i < items.length; i++) { yield items[i] } } } // 也可以 var obj = { *createIterator(items) { for (let i = 0; i < items.length; i++) { yield items[i] } } }
生成器会在每个yield 语句后停止执行,在函数中停止执行的能力是极其强大的,yield 关键字指定了迭代器在被调用next的方法是应当按顺序返回的值,在没有调用next() 方法的时候,生成器函数里的的代码并不会执行,同时也可用通过return 返回生成器函数的返回值,如下:
function* generatorFn() { console.log("start") yield 'foo'; yield; yield 'bar'; return 'baz'; } let generatorObject = generatorFn(); console.log(generatorObject.next()); //start { done: false, value: 'foo' } console.log(generatorObject.next()); // { done: false, value: undefined } console.log(generatorObject.next()); // { done: false, value: 'bar' } console.log(generatorObject.next()); // { done: true, value: 'baz' }
3、 yield关键字详解
(1) yield 关键字可以和值或者是表达式在一起使用,因此可以通过生成器给迭代器添加项目,而不是机械化地将项目一个个列出。
// for循环内部使用yield关键字 function* createIterator2(items) { //let 块级作用域 for (let i = 0; i < items.length; i++) { yield items[i] } } let iterator2 = createIterator2([1, 2, 3]) console.log(iterator2.next());//{value:1,done:false} console.log(iterator2.next());//{value:2,done:false} console.log(iterator2.next());//{value:3,done:false} console.log(iterator2.next());//{value:undefined,done:true}
注意 :yield 关键字只能用于生成器内部,用于其他位置会出现语法错误,即使在生成器内部的函数中也不行,下面的代码报错。
// yield无法穿越函数边界,在一个嵌套函数中无法将值返回给包含它的函数 // function* createIterator2(items) { // items.forEach(item => { // yield item +1//语法错误 // }); // }
(2) yield 关键字还可以作为函数的中间参数使用
使用 yield 实现输入和输出, yield 关键字还可以作为函数的中间参数使用,上一次让生成器函数暂停的 yield 关键字会接收到传给 next() 方法的第一个值。第一次调用 next() 传入的值不会被使用,因为这一次调用是为了开始执行生成器函数
function* generatorFn(initial) { console.log(initial); console.log(yield); console.log(yield); } let generatorObject = generatorFn('foo'); generatorObject.next('bar'); // foo generatorObject.next('baz'); // baz generatorObject.next('qux'); // qux // yield 关键字可以同时用于输入和输出,如下 function* generatorFn() { return yield 'foo'; } let generatorObject = generatorFn(); console.log(generatorObject.next()); // { done: false, value: 'foo' } console.log(generatorObject.next('bar')); // { done: true, value: 'bar' }
4、 yield* 委托给其他生成器或者可迭代对象
(1)yield* 委托给其他可迭代对象
可以使用星号增强 yield 的行为,让它能够迭代一个可迭代对象,从而一次产出一个值,因为 yield * 实际上只是将一个可迭代对象序列化为一连串可以单独产出的值,所以这跟把 yield 放到一个循环里没什么不同。下面两个生成器函数的行为是等价的:
function* generatorFnA() { for (const x of [1, 2, 3]) { yield x; } } console.log(generatorFnA()); for (const x of generatorFnA()) { console.log(x); } // 1 // 2 // 3 function* generatorFnB() { yield* [1, 2, 3]; } for (const x of generatorFnB()) { console.log(x); } // 1 // 2 // 3
(2)yield* 委托给其他生成器
function* createNumberIterator() { yield 1; yield 2; return 3; } function* createRepeatingIterator(count) { for (let i = 0; i < count; i++) { yield "repeat"; } } function* createCombinedIterator() { let result = yield* createNumberIterator(); yield result; yield* createRepeatingIterator(result); } var iterator = createCombinedIterator(); 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: "repeat", done: false }" console.log(iterator.next()); // "{ value: "repeat", done: false }" console.log(iterator.next()); // "{ value: "repeat", done: false }" console.log(iterator.next()); // "{ value: undefined, done: true }"
上面的代码中createCombinedIterator()生成器委托了 createNumberIterator 生成器,并将它的返回值赋值给了result变量,yeild result 输入该变量值3,result变量接下来作为参数传递 createRepeatingIterator()生成器,提示同一字符串需要调用3次。
写在最后
以上就是本文的全部内容,希望给读者带来些许的帮助和进步,方便的话点个关注,小白的成长之路会持续更新一些工作中常见的问题和技术点。