40.生成器和生成器函数
2021-11-24 本文已影响0人
静昕妈妈芦培培
生成器是ES6中新增的一种函数控制、使用方案,它可以让我们更加灵活的控制函数什么时候暂停执行、继续执行等
平时我们编写的普通函数,这些函数终止的条件通常是返回值或者发生了异常,但是一旦终止,无法继续执行
普通函数执行结果
function foo() {
console.log("函数开始执行");
const value1 = 100;
console.log(value1);
// 位置1
const value2 = 200;
console.log(value2);
// 位置2
const value3 = 300;
console.log(value3);
// 位置3
console.log("函数执行终止");
}
foo();
下面是执行结果:
函数开始执行
100
200
300
函数执行终止
如果我们希望函数执行到位置1暂停,即仅执行第一行代码
然后继续执行,到位置2暂停,
然后继续执行,到位置3暂停
然后继续执行到结束
可以使用生成器控制函数的执行
什么是生成器函数
生成器函数也是一个函数,但是和普通函数有一些区别:
- 生成器函数需要在function的后面加一个符号
*
- 生成器函数可以通过
yield
关键字来控制函数的执行过程 -
生成器函数执行会返回一个生成器(Generator)
- 生成器事实上是一种特殊的迭代器
例:创建一个生成器函数
function * foo() {
console.log("函数开始执行");
const value1 = 100;
console.log(value1);
yield; // 位置1
const value2 = 200;
console.log(value2);
yield; // 位置2
const value3 = 300;
console.log(value3);
yield; // 位置3
console.log("函数执行终止");
}
//执行生成器函数获取生成器
const generator = foo()
// 通过调用生成器的next方法去控制函数执行
generator.next()
console.log('------')
generator.next()
generator.next()
console.log('------')
generator.next()
执行结果如下:
函数开始执行
100
------
200
300
------
函数执行终止
生成器函数的执行流程
生成器函数调用会返回生成器,通过调用生成器的next方法,去控制生成器函数的执行
生成器的next方法执行会返回一个对象obj,此对象有done和value属性
生成器函数执行:
- 当遇到yeild就暂停执行, 此时obj.done为false,obj.value为yield后跟的值
- 当遇到return就终止执行, 此时obj.done为true,obj.value为return返回值
- 当函数中代码执行完也会终止执行, 此时obj.done为true,obj.value为return返回值
例1:
//创建一个生成器函数
function * foo() {
console.log("函数开始执行");
const value1 = 100;
console.log(value1);
yield; // 位置1
const value2 = 200;
console.log(value2);
yield; // 位置2
const value3 = 300;
console.log(value3);
yield; // 位置3
console.log("函数执行终止");
}
//执行生成器函数获取生成器
const generator = foo()
// 通过调用生成器的next方法去控制函数执行
console.log("执行结果1:", generator.next());
console.log("执行结果2:", generator.next());
console.log("执行结果3:", generator.next());
console.log("执行结果4:", generator.next());
执行结果:
函数开始执行
100
执行结果1: { value: undefined, done: false }
200
执行结果2: { value: undefined, done: false }
300
执行结果3: { value: undefined, done: false }
函数执行终止
执行结果4: { value: undefined, done: true }
例2:生成器函数中遇到return
//创建一个生成器函数
function * foo() {
console.log("函数开始执行");
const value1 = 100;
console.log(value1);
yield; // 位置1
return;
const value2 = 200;
console.log(value2);
yield; // 位置2
const value3 = 300;
console.log(value3);
yield; // 位置3
console.log("函数执行终止");
}
//执行生成器函数获取生成器
const generator = foo()
// 通过调用生成器的next方法去控制函数执行
console.log("执行结果1:", generator.next());
console.log("执行结果2:", generator.next());
console.log("执行结果3:", generator.next());
console.log("执行结果4:", generator.next());
执行结果:
函数开始执行
100
执行结果1: { value: undefined, done: false }
执行结果2: { value: undefined, done: true }
执行结果3: { value: undefined, done: true }
执行结果4: { value: undefined, done: true }
例3:return和yield后跟值
function* foo() {
console.log("函数开始执行");
const value1 = 100;
console.log(value1);
yield 800; // 位置1
const value2 = 200;
console.log(value2);
yield; // 位置2
return 700;
const value3 = 300;
console.log(value3);
yield; // 位置3
console.log("函数执行终止");
}
//执行生成器函数获取生成器
const generator = foo();
// 通过调用生成器的next方法去控制函数执行
console.log("执行结果1:", generator.next());
console.log("执行结果2:", generator.next());
console.log("执行结果3:", generator.next());
console.log("执行结果4:", generator.next());
执行结果:
函数开始执行
100
执行结果1: { value: 800, done: false }
200
执行结果2: { value: undefined, done: false }
执行结果3: { value: 700, done: true }
执行结果4: { value: undefined, done: true }
给生成器函数传参
- 给生成器函数的第一段代码传参,通过在调用生成器函数时,作为实参传入
- 给生成器函数的第二段及以上的代码传参,通过在调用生成器的next方法时给next传参,通过在上一段代码中的yeild表达式返回值接收
//创建一个生成器函数
function* foo(n1) {
const value1 = 100 * n1;
console.log(value1);
const n2 = yield value1; // 位置1
//第二段代码中使用的参数,通过在上一段代码中的yeild表达式的返回值接收
const value2 = 200 * n2;
console.log(value2);
const n3 = yield value2; // 位置2
const value3 = 300 * n3;
console.log(value3);
const n4 = yield value3; // 位置3
console.log("函数终止运行");
return n4
}
//执行生成器函数获取生成器
// 给生成器函数的第一段代码传参,通过在调用生成器函数时,作为实参传入
const generator = foo(1);
// 通过调用生成器的next方法去控制函数执行
console.log("执行结果1:", generator.next());
//第二段代码中使用的参数,通过在第二次调用生成器的next方法时作为参数传入
console.log("执行结果2:", generator.next(2));
console.log("执行结果3:", generator.next(3));
console.log("执行结果4:", generator.next('abc'));
执行结果:
100
执行结果1: { value: 100, done: false }
400
执行结果2: { value: 400, done: false }
900
执行结果3: { value: 900, done: false }
函数终止运行
执行结果4: { value: 'abc', done: true }
生成器的return函数
- 生成器使用return函数,来中止生成器函数的执行
- return函数执行返回一个obj对象,有done和value属性,done的值为true
- return函数执行时传入的参数,会作为obj.value的值
- 使用生成器调用return函数相当于在生成器函数执行到的当前位置,添加一个return,return返回的值为return函数执行时传入的参数
//创建一个生成器函数
function* foo(n1) {
const value1 = n1;
console.log(value1);
const n2 = yield value1; // 位置1
const value2 = n2;
console.log(value2);
const n3 = yield value2; // 位置2
const value3 = n3;
console.log(value3);
const n4 = yield value3; // 位置3
console.log("函数终止运行");
return n4
}
//执行生成器函数获取生成器
// 给生成器函数的第一段代码传参,通过在调用生成器函数时,作为实参传入
const generator = foo(1);
// 通过调用生成器的next方法去控制函数执行
console.log(generator.next());
console.log('return函数执行结果', generator.return('return'))
console.log(generator.next(2));
console.log(generator.next(3));
执行结果:
1
{ value: 1, done: false }
return函数执行结果 { value: 'return', done: true }
{ value: undefined, done: true }
{ value: undefined, done: true }
生成器的throw方法:抛出异常
- 生成器使用throw函数,来抛出异常
- throw函数执行没有返回值
- 使用生成器调用throw函数相当于在生成器函数执行到的当前位置,添加一个throw,抛出异常的错误为throw函数执行时传入的参数
- 如果使用try/catch把上一个yield那行代码包裹起来,使用throw抛出异常,会调用catch的回调参数,然后继续执行下一段代码
- 只要在代码生成器函数中有捕获执行throw抛出的异常,生成器可以通过调用next方法,继续执行下面的代码
- 如果没有在代码生成器函数中没有捕获执行throw抛出的异常,则中止生成器函数
//创建一个生成器函数
function* foo(n1) {
const value1 = n1;
console.log(value1);
try {
yield value1; // 位置1
} catch (error) {
console.log("捕获到异常", error);
}
console.log("第二段代码开始执行");
const value2 = 2;
console.log(value2);
yield value2; // 位置2
const value3 = 3;
console.log(value3);
yield value3; // 位置3
console.log("函数终止运行");
}
//执行生成器函数获取生成器
// 给生成器函数的第一段代码传参,通过在调用生成器函数时,作为实参传入
const generator = foo(1);
// 通过调用生成器的next方法去控制函数执行
console.log(generator.next());
console.log(generator.throw("错误原因"));
//只要在代码生成器函数中有捕获执行throw抛出的异常,生成器可以通过调用next方法,继续执行下面的代码
console.log(generator.next());
console.log(generator.next());
执行结果:
1
{ value: 1, done: false }
捕获到异常 错误原因
第二段代码开始执行
2
{ value: 2, done: false }
3
{ value: 3, done: false }
函数终止运行
{ value: undefined, done: true }
image.png
生成器替换迭代器
之前学习迭代器的时候,我们有封装一个返回一个数组的迭代器的函数
function createArrayIterator(arr) {
let index = 0;
return {
next() {
if (index < arr.length) {
return { done: false, value: arr[index++] };
} else {
return { done: true, value: undefined };
}
},
};
}
const names = ["lily", "koby"];
const namesIterator = createArrayIterator(names);
console.log(namesIterator.next()); // { done: false, value: 'lily' }
console.log(namesIterator.next()); // { done: false, value: 'koby' }
console.log(namesIterator.next()); // { done: true, value: undefined }
因为生成器是特殊的迭代器,而生成器函数的执行返回一个生成器,所以我们可以用生成器函数,控制对目标数组的遍访
创建一个生成器函数,函数执行返回一个目标数组的生成器
function* createArrayIterator(arr) {
for (const item of arr) {
yield item;
}
}
const names = ["lily", "koby"];
const namesIterator = createArrayIterator(names);
console.log(namesIterator.next()); // { done: false, value: 'lily' }
console.log(namesIterator.next()); // { done: false, value: 'koby' }
console.log(namesIterator.next()); // { done: true, value: undefined }
上面代码可以简写如下:
//创建一个生成器函数,函数执行返回一个目标数组的生成器
function* createArrayIterator(arr) {
//方法一:
// for (const item of arr) {
// yield item;
// }
//方法二:
// 在生成器函数中使用yield* 后面跟可迭代对象相当于上面代码
yield* arr;
}
const names = ["lily", "koby"];
const namesIterator = createArrayIterator(names);
console.log(namesIterator.next()); // { done: false, value: 'lily' }
console.log(namesIterator.next()); // { done: false, value: 'koby' }
console.log(namesIterator.next()); // { done: true, value: undefined }
例:创建一个函数,这个函数可以迭代一个范围内的数字
//
function createRangeIterator(start, end) {
let index = start;
return {
next() {
if (index < end) {
return { done: false, value: index++ };
} else {
return { done: true, value: undefined };
}
},
};
}
const rangeIterate = createRangeIterator(10, 15)
console.log(rangeIterate.next())
console.log(rangeIterate.next())
console.log(rangeIterate.next())
console.log(rangeIterate.next())
console.log(rangeIterate.next())
console.log(rangeIterate.next())
执行结果:
{ done: false, value: 10 }
{ done: false, value: 11 }
{ done: false, value: 12 }
{ done: false, value: 13 }
{ done: false, value: 14 }
{ done: true, value: undefined }
上面可以简写如下
function* createRangeIterator(start, end) {
let index = start;
while (index < end) {
yield index++;
}
}
const rangeIterate = createRangeIterator(10, 15);
console.log(rangeIterate.next());
console.log(rangeIterate.next());
console.log(rangeIterate.next());
console.log(rangeIterate.next());
console.log(rangeIterate.next());
console.log(rangeIterate.next());
例:自定义类的可迭代性
案例:创建一个Classroom类
教室中有自己的位置,名称,当前教室的学生
这个教室可以进来新学生(add)
创建的教室对象都是可迭代对象
class Classroom {
constructor(name, address, students) {
this.name = name;
this.address = address;
this.students = students;
}
add(stu) {
this.students.push(stu);
}
[Symbol.iterator] = function () {
let index = 0;
return {
next: () => {
if (index < this.students.length) {
return { done: false, value: this.students[index++] };
} else {
return { done: true };
}
},
};
};
}
const classroom = new Classroom("测控技术与仪器", "226", [
"lily",
"koby",
"curry",
]);
for (const stu of classroom) {
console.log(stu);
}
// lily
// koby
// curry
上面代码可简写为:
this.students.push(stu);
}
[Symbol.iterator] = function * () {
yield* this.students
};
}
const classroom = new Classroom("测控技术与仪器", "226", [
"lily",
"koby",
"curry",
]);
for (const stu of classroom) {
console.log(stu);
}
// lily
// koby
// curry
非常感谢王红元老师的深入JavaScript高级语法让我学习到很多 JavaScript
的知识