ES6 详解之 Generator

基本概念

Generator 函数有多种理解角度。语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。

执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。

Generator 本质是一个遍历器生成函数

形式上,Generator 函数是一个普通函数,但是有两个特征。

  • function 关键字与函数名之间有一个星号; function*()
  • 函数体内部使用 yield 表达式,定义不同的内部状态(yield 在英语里的意思就是 产出)。

    function* generator(){

yield 1;

yield 2;

yield 3;

return 'ending';

}

let hw = generator();

Generator 函数的调用方法与普通函数一样,也是在函数名后面加上一对圆括号。

不同的是:调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,也就是上一章介绍的遍历器对象(Iterator Object)。

下一步,必须调用遍历器对象的 next 方法,使得指针移向下一个状态。也就是说,每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个的yield表达式(或return语句)为止。换言之,Generator 函数是分段执行的,yield表达式是暂停执行的标记,而 next 方法可以恢复执行。

hw.next()

// { value: 1, done: false }

hw.next()

// { value: 2, done: false }

hw.next()

// { value: 'ending', done: true }

hw.next()

// { value: undefined, done: true }

总结一下

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

另外需要注意:yield 表达式只能用在 Generator 函数里面,用在其他地方都会报错。

(function (){

yield 1;

})()

// SyntaxError: Unexpected number

通过 yield* 来剥离复杂数据

{

let arr = [1,[2,3],[[4,5,6],7],[[8],[[9],[10]]]];

let flat = function* (a) {

let length = a.length;

for (let i=0;i<length;i++){

let item = a[i];

if(typeof item !== 'number') {

yield* flat(item);

} else {

yield item;

}

}

};

for (var f of flat(arr)) {

console.log(f); // 1,2,3,4,5,6,7,8,9,10

}

}

与 Iterator 接口的关系

任意一个对象的 Symbol.iterator 方法,等于该对象的遍历器生成函数,调用该函数会返回该对象的一个遍历器对象.

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

{

let myInterable = {};

myInterable[Symbol.iterator] = function* (){

yield 1;

yield 2;

yield 3;

};

for (const value of myInterable) {

console.log(value); // 1,2,3

}

console.log([...myInterable]); // [1,2,3]

}

next 方法的参数

yield 表达式本身没有返回值,或者说总是返回 undefined。next 方法可以带一个参数,该参数就会被当作上一个 yield 表达式的返回值。

function* f() {

for(var i = 0; true; i++) {

var reset = yield i;

if(reset) { i = -1; }

}

}

var g = f();

g.next() // { value: 0, done: false }

g.next() // { value: 1, done: false }

g.next(true) // { value: 0, done: false }

另外一个例子:

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 方法提供参数,返回结果就完全不一样了。上面代码

第一次调用 b 的 next 方法时,x的值为5,返回x+1的值6;

第二次调用next方法,将上一次yield表达式的值设为12,因此y等于24,返回y / 3的值8;

第三次调用next方法,将上一次yield表达式的值设为13,因此z等于13,这时x等于5,y等于24,所以return语句的值等于42。

下面是一个利用 Generator 函数和 for…of 循环,实现斐波那契数列的例子。

{

function* fiboncci() {

let [prev,curr] = [0,1];

for(;;){

yield curr;

[prev,curr] = [curr,prev + curr];

}

}

let array = [];

for (let n of fiboncci()) {

if(n>1000) break;

array.push(n);

}

console.log(array); // [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987]

}

Generator.prototype.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 }

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

上面代码中,遍历器对象g调用return方法后,返回值的value属性就是return方法的参数foo。并且,Generator 函数的遍历就终止了,返回值的done属性为true,以后再调用next方法,done属性总是返回true。

注意点:如果 Generator 函数内部有 try…finally 代码块,且正在执行 try 代码块,那么 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 }

以上是 ES6 详解之 Generator 的全部内容, 来源链接: utcz.com/z/264120.html

回到顶部