生成器(是一种特殊的iterator)[特殊的迭代器]

108 阅读3分钟

基本生成器的各种使用

function* foo() {
  // function*、function [n*n] {name}、function *{name}就是生成器函数
  console.log('生成器函数开始执行~');
  // yield关键字会暂停生成器函数的执行(且可返回值)
  yield;
  const v1 = 1;
  console.log(v1);

  yield;
  const v2 = 2;
  console.log(v2);

  yield;
  const v3 = 3;
  console.log(v3);

  console.log('生成器函数执行结束~');
};
// 生成器函数直接调用不会执行内部代码
// foo();

// 生成器
const generator = foo();
// 执行生成器函数内第一段代码(第一个yield上面的代码)
generator.next();
// 执行生成器函数内第二段代码(第二个yield上面的代码)
generator.next();
// 执行生成器函数内第三段代码(第三个yield上面的代码)
generator.next();
// 执行生成器函数内最后一段代码(最后一个yield下面的代码)
generator.next();

function * bar() {
  console.log('bar生成器函数开始执行~');

  const v1 = 1, v2 = 2, v3 = 3;

  console.log(v1);
  console.log(v2);
  console.log(v3);

  console.log('bar生成器函数执行结束~');
};
// 因为内部不存在yield所以只需要调用一次next生成器内部代码就会全部执行
bar().next();

// 沙箱使用生成器
const generator = (function* () {
  console.log('可以log generator看看');
})();
console.log(generator);
// 先执行generator再打印结果
console.log(generator.next());

(function* () {
  console.log('链式调用');
})().next();

生成器的返回值

function* foo() {
  console.log('生成器函数开始执行~');
  // yield和return的区别是yield不会结束生成器函数而是暂停生成器函数(共同点是都可以返回值)
  yield 'yield返回的值';

  console.log('生成器函数执行结束~');
  // 函数不写返回值等同于返回undefined
  // return undefined;
  return '返回的值会被iterator当作返回的value值';
};
const generator = foo();
// 返回的值都会当作iterator值{ done: status, value }
console.log(generator.next());
// 因为生成器返回了值所以这里执行后是有值的
console.log(generator.next());
// 所以执行完了会返回undefined
console.log(generator.next());

生成器接收参数

function* foo(params) {
  console.log(params);
  // 参数会被当做返回值接收
  const variable1 = yield 'yield1';
  console.log(variable1);

  const variable2 = yield 'yield' + variable1;
  console.log(variable2);
  // 返回给最后一次有效调用
  return variable1 + variable2;
};
const generator = foo(1);
// 第一次执行是调用生成器(即使传值也是徒劳的)
const result1 = generator.next(),
  result2 = generator.next(2),
  result3 = generator.next(3);

console.log(result1, result2, result3, generator.next());

生成器调用return、throw

function* foo() {
  try {
    console.log('start');

    // 下面调用return相当于在这里 return 'return';
    yield;

    console.log('在我上面的yield调用return、throw我都不能执行了');

    // 到这个yield主动抛出可异常
    yield;
    console.log('end');
  } catch (err) {
    // throw传的值会到catch里来
    console.error(err);

    // 返回给throw的iterator
    yield '害';
  }
};
// 调用return
const generator1 = foo();
generator1.next();
// 调用return以后的代码压根不会执行
console.log(generator1.return('return'));

// 调用throw
const generator2 = foo();
generator2.next();
generator2.next();
console.log(generator2.throw('让你执行完我再throw好吧!'));

生成器替代迭代器

function* arrIterator(arr) {
  for (const item of arr) {
    yield item;
  }
};
const iterator = arrIterator(['苏苏', '土狗', 'SharkDog'])

console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());

上面写法的语法糖

function* arrIterator(arr) {
  // yield* 跟上可迭代对象会自动全部迭代每次next返回
  yield* arr
};
const iterator = arrIterator(['苏苏', '土狗', 'SharkDog'])

console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());

生成器迭代指定返回内的所有数字

function* createNumber(start, end) {
  while (start < end) yield start++;
};
const iterator = createNumber(2, 8);

console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());

生成器做可迭代类

class SelfInfo {
  constructor(address, roomName, person) {
    this.address = address;
    this.roomName = roomName;
    this.person = person;
  };

  // [Symbol.iterator] = function* () {
  //   yield* this.person;
  // }

  // 类中生成器函数的语法糖
  *[Symbol.iterator]() {
    yield* this.person;
  }
};
const info = new SelfInfo('合肥', 'E5-1804', ['苏苏', '土狗', 'SharkDog']);

for (const iterator of info) console.log(iterator);

对象中使用生成器函数

const obj = {
  arr: [1, 2, 3, 5, 8],
  *foo() {
    yield* this.arr;
  }
};
const testObj = obj.foo();

for (const iterator of testObj) console.log(iterator);
console.log(testObj.next());
console.log(testObj.next());
console.log(testObj.next());
console.log(testObj.next());
console.log(testObj.next());
console.log(testObj.next());