ES6笔记之generator函数以及实现对象的遍历器接口

一、Generator函数简介

  1. 执行 Generator 函数会返回一个遍历器对象
  2. function关键字与函数名之间有一个星号function * f () {…}
  3. 函数体内部使用yield表达式,定义不同的内部状态
function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}

var hw = helloWorldGenerator(); //函数并不执行,而是返回遍历器对象;

hw.next()
// { value: 'hello', done: false }

hw.next()
// { value: 'world', done: false }

hw.next()
// { value: 'ending', done: true }

hw.next()
// { value: undefined, done: true }

以上代码有三个状态,hello、world、ending,通过调用hw的next方法,hw便由函数头部或上一次停下来的地方开始执行,直到遇到下一个yield表达式(或return语句)为止,Generator 函数从暂停状态到恢复运行,它的上下文状态(context)是不变的。

总结一下,调用 Generator 函数,返回一个遍历器对象,代表 Generator 函数的内部指针。以后,每次调用遍历器对象的next方法,就会返回一个有着value和done两个属性的对象。value属性表示当前的内部状态的值,是yield表达式后面那个表达式的值;done属性是一个布尔值,表示是否遍历结束。

二、next传递参数

function* foo(x) {
  var y = 2 * (yield (x + 1));
  var z = yield (y / 3);
  return (x + y + z);
}

var a = foo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false}
a.next() // Object{value:NaN, done:true}

var b = foo(5);
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false }
b.next(13) // { value:42, done:true }

由于next方法的参数表示上一个yield表达式的返回值,所以在第一次使用next方法时,传递参数是无效的

无参数的情况下:
第一运行用next方法,系统自动忽略参数,返回x+1的值6;
第二次运行next方法的时候不带参数,导致 y 的值等于2 * undefined(即NaN),因此返回对象的value属性(y/3)也等于NaN;
第三次运行next方法的时候不带参数,所以z等于undefined,返回对象的value属性等于5 + NaN + undefined,即NaN。

有参数的情况下:
第一运行用next方法,系统自动忽略参数,返回x+1的值6;
第二次调用next方法,将上一次yield表达式的值设为12,因此y等于24,返回y / 3的值8;
第三次调用next方法,将上一次yield表达式的值设为13,因此z等于13,这时x等于5,y等于24,所以return语句的值等于42。

三、throw方法
Generator 函数返回的遍历器对象,都有一个throw方法(不同于全局throw指令),可以在函数体外抛出错误,然后在 Generator 函数体内捕获。throw方法抛出的错误要被内部捕获,前提是必须至少执行过一次next方法。

var g = function* () {
  try {
    yield;
  } catch (e) {
    console.log('内部捕获', e);
  }
};

var i = g();
i.next();

try {
  i.throw(new Error('出错了!'));
  i.throw('b');
} catch (e) {
  console.log('外部捕获', e);
}
// 内部捕获 Error: 出错了!
// 外部捕获 b

catch语句捕获。i第二次抛出错误,由于 Generator 函数内部的catch语句已经执行过了,不会再捕捉到这个错误了,所以这个错误就被抛出了 Generator 函数体,被函数体外的catch语句捕获。

四、return方法
Generator 函数返回的遍历器对象,还有一个return方法,可以返回给定的值,并且终结遍历 Generator 函数。

function* gen() {
  yield 1;
  yield 2;
  yield 3;
}

var g = gen();

g.next()        // { value: 1, done: false }
g.return('foo') // { value: "foo", done: true },不带参数则value为undefined
g.next()        // { value: undefined, done: true }

如果 Generator 函数内部有try…finally代码块,那么return方法会推迟到finally代码块执行完再执行。

function* numbers () {
  yield 1;
  try {
    yield 2;
    yield 3;
  } finally {
    yield 4;
    yield 5;
  }
  yield 6;
}
var g = numbers();
g.next() // { value: 1, done: false }
g.next() // { value: 2, done: false }
g.return(7) // { value: 4, done: false }
g.next() // { value: 5, done: false }
g.next() // { value: 7, done: true }

五、next、throw、return的共同的点
next()是将yield表达式替换成一个值。
throw()是将yield表达式替换成一个throw语句。
return()是将yield表达式替换成一个return语句。

六、Generator函数实现对象遍历器接口

var myIterable = {
    a : 1,
    b : 2
};
myIterable[Symbol.iterator] = function* (obj = myIterable) {
 let propKeys = Object.keys(obj);

  for (let propKey of propKeys) {
    yield [propKey, obj[propKey]];
  }
};

[...myIterable] // [['a', 1], ['b', 2]]

由于 Generator 函数就是遍历器生成函数,因此可以把 Generator 赋值给对象的Symbol.iterator属性,从而使得该对象具有 Iterator 接口。

function* objectEntries(obj) {
  let propKeys = Reflect.ownKeys(obj);

  for (let propKey of propKeys) {
    yield [propKey, obj[propKey]];
  }
}

let jane = { first: 'Jane', last: 'Doe' };

for (let [key, value] of objectEntries(jane)) {
  console.log(`${key}: ${value}`);
}
// first: Jane
// last: Doe

以上方法,直接将对象转化成iterator遍历器对象,以便直接使用for … of 语句。

猜你喜欢

转载自blog.csdn.net/qq_36470086/article/details/82470500
今日推荐