JSJs

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 * 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属性

生成器函数执行:

例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 }

给生成器函数传参


//创建一个生成器函数
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函数

//创建一个生成器函数
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方法:抛出异常



//创建一个生成器函数
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 的知识

上一篇下一篇

猜你喜欢

热点阅读