美文网首页JSJs
40.生成器和生成器函数

40.生成器和生成器函数

作者: 静昕妈妈芦培培 | 来源:发表于2021-11-24 13:45 被阅读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 的知识

    相关文章

      网友评论

        本文标题:40.生成器和生成器函数

        本文链接:https://www.haomeiwen.com/subject/btlntrtx.html