add: 生成器

This commit is contained in:
qianguyihao 2023-05-28 20:30:46 +08:00
parent 3f964b6467
commit 7fb0fdb0fc
1 changed files with 190 additions and 2 deletions

View File

@ -495,7 +495,7 @@ const myObj2 = {
return { done: true };
}
},
// 【重要】监听迭代器的中断
// 【关键代码】监听迭代器的中断
return: () => {
console.log('迭代器被中断了');
return { done: true };
@ -510,7 +510,7 @@ for (const item of myObj2) {
const [key, value] = item;
console.log(key, value);
if (value == 'qianguyihao') {
// 【重要】如果发现 value 为 qianguyihao则中断迭代器停止继续遍历
// 【关键代码】如果发现 value 为 qianguyihao则中断迭代器停止继续遍历
break;
}
}
@ -525,6 +525,194 @@ name qianguyihao
根据打印结果可以看出,迭代器只遍历了 myObj2 对象的第一个元素,符合指定条件后,通过 break 语句中断了迭代器,停止了继续遍历;与此同时,迭代器中的 return() 函数监听到了迭代器的中断。对了return() 函数中,还需要写 `return { done: true }`表示迭代器的使命已结束;如果不写这行则会报错:`Uncaught TypeError: Iterator result undefined is not an object`。
## 生成器
### 概念
我们平时写的函数,基本是通过 return 返回值,或者发生异常,函数才会终止执行。这还不够灵活。
**生成器**是 ES6 中新增的一种特殊的函数,所以也称为“生成器函数”。它可以更灵活地控制函数什么时候执行, 什么时候暂停等等。
生成器函数使用 `function*` 语法编写。最初调用时,生成器函数不执行任何代码,而是返回一个称为 Generator 的迭代器。通过调用生成器的 next() 方法时Generator 函数将执行,直到遇到 yield 关键字时暂停执行。
可以根据需要多次调用该函数,并且每次都返回一个新的 Generator但每个 Generator 只能迭代一次。
### 生成器函数和普通函数的区别
- 生成器函数需要在 `function` 关键字后面加一个符号 `*`
- 生成器函数可以通过 `yield` 关键字控制函数的执行流程。
- 生成器函数的返回值是一个生成器Generator。生成器是一种特殊的迭代器。
## 生成器函数拆解
### 定义一个生成器函数,以及生成器函数的执行时机
如果要定义一个生成器函数,我们需要在`function`单词的后面追加一个`*`符号。代码举例:
```js
function* foo() {
console.log('1');
console.log('2');
console.log('3');
}
foo();
```
上方代码中,`*`符号既可以写在 `function`单词的后面,即`function* foo()`;也可以写在`foo`单词的前面,即`function *foo()`。这两种写法都可以,第一种写法更常见。
但是上面的代码写完后,并不会有打印结果,因为**我们还需要调用生成器的 next()方法,生成器函数才会执行,直到遇到 yield 关键字后暂停执行****最后遇到 return关键字或者遇到函数末尾时结束执行**。代码举例:
```js
// 通过 * 符号,定义一个生成器函数
function* foo() {
console.log('1');
yield;
console.log('2');
// 下面这行 console.log('a') 会跟 yield 一起执行
yield console.log('a');
console.log('3');
}
const generator = foo(); // 返回一个生成器对象
// 调用生成器的 next()方法,生成器才会执行,直到遇到 yield 后暂停执行
generator.next(); // 这行代码执行后打印结果是1
generator.next(); // 这行代码执行后打印结果是1 2 a
generator.next(); // 这行代码执行后打印结果是1 2 a 3
```
仔细看注释,生成器 generator 每调用一次 next() foo()函数里的代码就往下执行一次,直到遇到 yield 后暂停。
### next() 方法的返回值
生成器既然是一种特殊的迭代器,那么它也有 next()方法,而且 next()方法里同样有 done 和 value 这两个属性。我们来看看这两个属性的**默认属性值**是什么:
```js
// 通过 * 符号,定义一个生成器函数
function* foo() {
console.log('阶段1');
yield;
console.log('阶段2');
yield;
console.log('阶段3');
return; // 执行 return 之后,函数不再继续往下走了,生成器的 next()方法的 done 属性值为 true。
console.log('阶段4');
}
// 执行生成器函数,返回一个生成器对象
const generator = foo();
// 调用生成器的 next()方法,生成器才会执行,直到遇到 yield 后暂停执行;遇到 return关键字或者遇到函数末尾时结束执行。
console.log(generator.next());
console.log(generator.next());
console.log(generator.next());
```
打印结果:
```
阶段1
{value: undefined, done: false}
阶段2
{value: undefined, done: false}
阶段3
{value: undefined, done: true}
```
上方代码的打印结果可以看出,生成器函数在遇到函数的末尾,或者遇到 return 之后函数就不再继续往下走了next()方法的 done 属性值为 true。
还可以看到next()方法的 value 属性值默认为 undefined**如果某些情况下我们希望 value属性有值**的话,可以通过 yield 关键字进行传递。代码举例:
```js
// 通过 * 符号,定义一个生成器函数
function* foo() {
console.log('阶段1');
yield 'a'; // 【关键代码】yield 后面写的内容,就是传递给 next() 方法的 value 属性值
console.log('阶段2');
yield 'b';
console.log('阶段3');
return; // 这里的 return相当于 return undefined
console.log('阶段4');
}
// 执行生成器函数,返回一个生成器对象
const generator = foo();
// 调用生成器的 next()方法,生成器才会执行,直到遇到 yield 后暂停执行;遇到 return关键字或者遇到函数末尾时结束执行。
console.log(generator.next()); // 打印生成器对象的 next()方法
console.log(generator.next());
console.log(generator.next());
```
打印结果:
```
阶段1
{value: 'a', done: false}
阶段2
{value: 'b', done: false}
阶段3
{value: undefined, done: true}
```
### next() 方法的参数
根据前面的代码实例得知,生成器函数是分多个阶段执行的。此时有一个诉求:如何给当前阶段传递参数呢?
答案是:可以通过当前阶段 next() 方法的参数,给当前阶段传值。这个参数值会成为**上一个阶段** yield 语句的返回值。
代码举例:
```js
// 通过 * 符号,定义一个生成器函数
function* foo() {
console.log('阶段1');
// 【关键代码】第二次调用 next()方法时,通过 res2 接收 next()方法的参数值
const res2 = yield 'a';
console.log('阶段2:', res2);
// 第三次调用 next()方法时,通过 res3 接收 next()方法的参数值
const res3 = yield 'b';
console.log('阶段3:', res3);
return;
}
// 执行生成器函数,返回一个生成器对象
const generator = foo();
// 调用生成器的 next()方法,生成器才会执行,直到遇到 yield 后暂停执行;遇到 return关键字或者遇到函数末尾时结束执行。
console.log(generator.next()); // 执行第一阶段
console.log(generator.next('next2')); // 执行第二阶段,并传参
console.log(generator.next('next3')); // 指定第三阶段
```
打印结果:
```
阶段1
{value: 'a', done: false}
阶段2: next2
{value: 'b', done: false}
阶段3: ntext3
{value: undefined, done: true}
```
在理解上方代码时需要注意的是,将 next2 这个属性值赋值给 res2这个操作的执行时机是在**第二阶段的最开始**做的,不是在第一阶段的末尾做的。并且,这个属性值是通过第一阶段的 yield 返回值接收的。