什么是生成器

生成器是一个通过构造函数Generator创建的对象,生成器既是一个迭代器,同时又是一个可迭代对象
image.png
如图中 函数表达式c的隐士原型为 Generator 且隐士原型中还拥有next函数,next函数为迭代器,同时隐士原型中还拥有符号属性Symbol.iterator 该函数为可迭代对象

生成器的出现是为啦更加方便的书写迭代器

示例

  1. const arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
  2. // 迭代器创建函数 iterator creator
  3. function createIterator(arr) {
  4. let i = 0;//当前的数组下标
  5. return {
  6. next() {
  7. var result = {
  8. value: arr[i],
  9. done: i >= arr.length
  10. }
  11. i++;
  12. return result;
  13. }
  14. }
  15. }
  16. const iter1 = createIterator(arr1);

当使用生成器后

  1. const arr = [2, 3, 5, 4, 9]
  2. function* arrIterater() {
  3. for (const iterator of arr) {
  4. yield iterator;
  5. }
  6. }
  7. const a = arrIterater()

可以直接发现,每次写迭代器就很繁琐 , 尤其是书写可迭代对象,可迭代对象的格式看迭代器的笔记

如何创建与书写生成器

生成器的创建,必须使用生成器函数(Generator Function)
书写生成器的的格式

  1. //这是一个生成器函数,该函数一定返回一个生成器
  2. function* method(){
  3. yield "产生一个迭代数据"
  4. }

生成器的内部如何执行

生成器函数内部是为了给生成器的每次迭代提供的数据
每次调用生成器的next方法,将导致生成器函数运行到下一个yield关键字位置
yield是一个关键字,该关键字只能在生成器函数内部使用,表达“产生”一个迭代数据。

需注意的细节

1). 生成器函数可以有返回值,返回值出现在第一次done为true时的value属性中
2). 调用生成器的next方法时,可以传递参数,传递的参数会交给yield表达式的返回值
3). 第一次调用next方法时,传参没有任何意义
4). 在生成器函数内部,可以调用其他生成器函数,但是要注意加上*号
示例

  1. function* test() {
  2. console.log("第1次运行")
  3. yield 1;
  4. console.log("第2次运行")
  5. yield 2;
  6. console.log("第3次运行")
  7. }
  8. const generator = test();

其在控制台中的执行效果
image.png
生成器函数可以有返回值,返回值出现在第一次done为true时的value属性中

  1. function* Generator() {
  2. console.log("第1次运行")
  3. yield 1;
  4. console.log("第2次运行")
  5. yield 2 ;
  6. console.log("第3次运行")
  7. return "生成器函数可以有返回值,返回值出现在第一次done为true时的value属性中"
  8. }
  9. const g = Generator()

image.png
从控制台的打印效果可以看出,在第三次迭代数据中返回的值为规定结束时返回的值,但是当第四次迭代数据的返回值却为unfinished 这说明返回值值出现在第一次done为true时的value属性中
调用生成器的next方法时,可以传递参数,传递的参数会交给yield表达式的返回值 / 第一次调用next方法时,传参没有任何意义

  1. function* Generator() {
  2. console.log("第1次运行")
  3. let info = yield 1;
  4. console.log(info ,"-info的值")
  5. console.log("第2次运行")
  6. yield 2 + info;
  7. console.log(info,"-info的值")
  8. console.log("第3次运行")
  9. }
  10. const g = Generator()

如上面代码中 第二次迭代数据中有为未知的数据(info),当往里传入参数时,会发生何种效果
image.png
如上图的控制打印效果,传入参数 第一次迭代的数据没有任何效果,到啦第二次就有效果啦这是为什么
内部执行原理
g.next(50) 第一次迭代效果 ,传递的参数会交给yield表达式的返回值 ,那就是说 先执行yield 1;此时let info = yield 1;这句话只执行啦一半并还没有给info赋值,然后到第二次迭代传递的参数会交给yield表达式的返回值,那么 yield 1;的返回值就会是50,且赋值给info,这也就是第二次赋值结果为啥是52 这也说明 第一次调用next方法时,传参没有任何意义
在生成器函数内部,可以调用其他生成器函数,但是要注意加上*号

  1. function* t1(){
  2. yield "a"
  3. yield "b"
  4. }
  5. function* test() {
  6. yield* t1();
  7. yield 1;
  8. yield 2;
  9. yield 3;
  10. }
  11. const generator = test();

image.png
从控制台执行效果可以看到先执行生成器函数t1()等到t1()执行完才执行的text()

生成器的其他api

  • return方法:调用该方法,可以提前结束生成器函数,从而提前让整个迭代过程结束
    - throw方法:调用该方法,可以在生成器中产生一个错误

    1. function* Generator() {
    2. console.log("第1次运行")
    3. yield 1;
    4. console.log("第2次运行")
    5. yield 2;
    6. console.log("第3次运行")
    7. }
    8. const g = Generator()

    image.png
    如图 在其控制台的打印效果,第二次的迭代中使用return 方法将其提前结束生成器函数,之后继续调动next方法返回都是unfinished的值
    image.png
    如上图,第一次正常迭代数据,第二次使用throw方法爆出错误,这个错误是故意爆出的错误,其代码并没有错误,之后继续调用next方法与return的返回数据结果一样

    案例

    1. // 斐波拉契数列
    2. // 1 1 2 3 5 8 13
    3. function* createFeiBoLaQiIterater() {
    4. let prev1 = 1,
    5. prev2 = 1;
    6. let n = 1;
    7. while (true) {
    8. if (n <= 2) {
    9. yield 1
    10. } else {
    11. let newPrev = prev1 + prev2
    12. yield newPrev;
    13. prev2 = prev1;
    14. prev1 = newPrev;
    15. }
    16. n++
    17. }
    18. }
    19. const c = createFeiBoLaQiIterater()