◼ 生成器是ES6中新增的一种函数控制、使用的方案,它可以让我们更加灵活的控制函数什么时候继续执行、暂停执行等。
平时我们会编写很多的函数,这些函数终止的条件通常是返回值或者发生了异常。
◼ 生成器函数也是一个函数,但是和普通的函数有一些区别:
首先,生成器函数需要在function的后面加一个符号:*
其次,生成器函数可以通过yield关键字来控制函数的执行流程:
最后,生成器函数的返回值是一个Generator(生成器):
✓ 生成器事实上是一种特殊的迭代器;
✓ MDN:Instead, they return a special type of iterator, called a Generator.
生成器函数的使用:
/*生成器函数特点:
1.function后面会加上符合:* function*
2.代码的执行可以被yield控制
3.生成器函数在执行时,返回一个生成器对象
*要想执行函数内部代码,需要生成器对象调用他的next操作
*当遇到yield时就会中断执行
*/
function* foo(){
console.log("11111")
console.log("22222")
yield console.log("aaaaa")
console.log("33333")
console.log("44444")
yield
console.log("55555")
console.log("66666")
}
// 调用生成器函数会返回一个生成器对象
const generator = foo()
// 调用next方法
generator.next()
generator.next()
generator.next()
生成器传递参数–next函数
◼ 函数既然可以暂停来分段执行,那么函数应该是可以传递参数的,我们是否可以给每个分段来传递参数呢?
答案是可以的;
我们在调用next函数的时候,可以给它传递参数,那么这个参数会作为上一个yield语句的返回值;
注意:也就是说我们是为本次的函数代码块执行提供了一个值;
function* foo(name1){
console.log("执行内部代码11111",name1)
console.log("执行内部代码1111122222",name1)
const name2 = yield "aaaa"
console.log("执行内部代码1111133333",name2)
console.log("执行内部代码1111144444",name2)
const name3 = yield "bbbb"
console.log("执行内部代码1111155555",name3)
console.log("执行内部代码1111166666",name3)
}
// 调用生成器函数会返回一个生成器对象
const generator = foo("第一次调用next")
// 调用next方法-->拿返回值
// console.log(generator.next())//{value: 'aaaa', done: false}
// console.log(generator.next())//{value: 'bbbb', done: false}
// console.log(generator.next())//{value: undefined, done: true}
// // 再中间位置直接return 结果-->拿返回值
// console.log(generator.next())//{value: 'aaaa', done: false}
// console.log(generator.next())//{value: 'bbbb', done: true}
// console.log(generator.next())//{value: undefined, done: true}
//4.给函数每次执行传入参数
console.log(generator.next())//{value: 'aaaa', done: false} -->执行内部代码11111 第一次调用next
console.log(generator.next("第二次调用next"))//{value: 'aaaa', done: false} -->执行内部代码1111133333 第二次调用next
console.log(generator.next("第三次调用next"))//{value: undefined, done: true}-->执行内部代码1111166666 第三次调用next
生成器提前结束–return函数
◼ 还有一个可以给生成器函数传递参数的方法是通过return函数:
return传值后这个生成器函数就会结束,之后调用next不会继续生成值了;
function* foo(name1){
console.log("执行内部代码11111",name1)
console.log("执行内部代码1111122222",name1)
const name2 = yield "aaaa"
console.log("执行内部代码1111133333",name2)
console.log("执行内部代码1111144444",name2)
const name3 = yield "bbbb"
console.log("执行内部代码1111155555",name3)
console.log("执行内部代码1111166666",name3)
yield
console.log("最后一次执行")
return undefined
}
//1.generator.return()提前结束
const generator = foo("nex1")
// console.log(generator.next())
// console.log(generator.return("next2"))
// console.log("-----------------")
// console.log(generator.next("next3"))
// console.log(generator.next("next4"))
生成器抛出异常–throw函数
◼ 除了给生成器函数内部传递参数之外,也可以给生成器函数内部抛出异常:
抛出异常后我们可以在生成器函数中捕获异常;
但是在catch语句中不能继续yield新的值了,但是可以在catch语句外使用yield继续中断函数的执行;
function* foo(){
console.log("函数开始执行~")
try{
yield "hdc"
}catch(err){
console.log("内部异常捕获:",err)
}
yield 222
console.log("函数执行结束")
}
生成器替代迭代器
◼ 我们发现生成器是一种特殊的迭代器,那么在某些情况下我们可以使用生成器来替代迭代器:
const names = ["aaa","bbb","ccc"]
const nums = [66,88,555,111]
function* createArrayIterator(arr){
for (let i = 0;i<arr.length;i++){
yield arr[i]
}
}
const namesIterator = createArrayIterator(names)
console.log(namesIterator.next())
console.log(namesIterator.next())
console.log(namesIterator.next())
console.log(namesIterator.next())
const numsIterator = createArrayIterator(nums)
事实上我们还可以使用yield*来生产一个可迭代对象:
这个时候相当于是一种yield的语法糖,只不过会依次迭代这个可迭代对象,每次迭代其中的一个值;
yield*必须放到生成器函数中
const names = ["aaa","bbb","ccc"]
const nums = [66,88,555,111]
function* createArrayIterator(arr){
for (let i = 0;i<arr.length;i++){
yield* arr
}
}
const namesIterator =createArrayIterator(names)
console.log(namesIterator.next())
console.log(namesIterator.next())
console.log(namesIterator.next())
console.log(namesIterator.next())
自定义类迭代–生成器实现
class ClassRoom{
constructor(address,name,students){
this.address = address
this.name = name
this.students = students
}
push(student){
this.students.push(student)
}
*[Symbol.iterator](){
yield* this.students
}
}
const classRoom1 = new ClassRoom("dhdhd","ddddd",["aaaa","bbbb","vvvvv"])
const PIteratro = classRoom1[Symbol.iterator]()
console.log(PIteratro.next())
console.log(PIteratro.next())
console.log(PIteratro.next())
console.log(PIteratro.next())