了解es6之Generator函数

基本概念

Generator 函数是一个状态机,封装了多个内部状态。执行 Generator 函数会返回一个遍历器对象,返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。

(Generator 生产者,yield产出)

特征function关键字与函数名之间有一个星号

​ 函数体内部使用yield表达式,定义不同的内部状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function* fn(){ //定义了3个状态hello,world,和ending(结束执行)
yield 'hello'
yield 'world'
return 'ending'
}
-----------
fn() //返回的是遍历器对象Iterator Object,并不会执行函数,必须调用next函数,开始执行第一个yield表达式,next()方法返回一个对象。value属性表示yield表达式的值,done表示是否完成了遍历
fn().next() //{value: "hello", done: false}
fn().next() //{value: "world", done: false}
需要重新声明赋值给一个变量var a=fn()
a.next() // { value: 'hello', done: false }
a.next() // { value: 'world', done: false }
a.next() // { value: 'ending', done: true }
a.next() // { value: undefined, done: true }
----------------
或者
var a=(function* (){
yield 'hello'
yield 'world'
return 'ending'
})()
a.next() //{value: "hello", done: false}
a.next() //{value: "world", done: false}

yield表达式

由于 Generator 函数返回的遍历器对象,只有调用next方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。yield表达式就是暂停标志

  • yield只能在Generator函数里面有效

  • yield没有返回值,或者总是返回undefined,next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。(所以第一次使用next()方法传递参数是无效的,因为他表示的是上一个yield表达式的返回值)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    function* foo(){
    var y = 2*(yield(x+1))
    var z= yield(y/3)
    return (x+y+3)
    }
    var a = foo(5);
    a.next() // Object{value:6, done:false} //返回yield后面式子的值
    a.next() // Object{value:NaN, done:false} //第二次运行next方法的时候不带参数,导致y的值等于2 * undefined(即NaN)
    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 }
  • yield表达式如果用在另一个表达式之中,必须放在圆括号里面。

    1
    2
    3
    4
    5
    6
    7
    function* demo() {
    console.log('Hello' + yield); // SyntaxError
    console.log('Hello' + yield 123); // SyntaxError
    console.log('Hello' + (yield)); // OK
    console.log('Hello' + (yield 123)); // OK
    }
  • yield表达式用作函数参数或放在赋值表达式的右边,可以不加括号。

    1
    2
    3
    4
    function* demo() {
    foo(yield 'a', yield 'b'); // OK
    let input = yield; // OK
    }
1
2
3
4
5
6
7
8
9
function* f() {
console.log('执行了!') Generator函数不用yield表达式,变成了暂缓执行函数
}
var generator = f() //一般函数到这一步赋值就会执行
setTimeout(function () {
generator.next()
}, 2000);
  • 从语法角度看,如果yield表达式后面跟的是一个遍历器对象,需要在yield表达式后面加上星号,表明它返回的是一个遍历器对象。这被称为yield*表达式。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    function* inner() {
    yield 'hello!';
    }
    function* outer1() {
    yield 'open';
    yield inner();
    yield 'close';
    }
    var gen = outer1()
    gen.next().value // "open"
    gen.next().value // 返回一个遍历器对象
    gen.next().value // "close"
  • 如果yield*后面跟着一个数组,由于数组原生支持遍历器,因此就会遍历数组成员。

    1
    2
    3
    4
    5
    6
    function* gen(){
    yield* ["a", "b", "c"];
    }
    var a = gen()
    a.next() //{value: "a", done: false}
    a.next() //{value: "b", done: false}
  • 实际上,任何数据结构只要有 Iterator 接口,就可以被yield*遍历。

    1
    2
    3
    4
    5
    6
    7
    let read = (function* () {
    yield 'hello';
    yield* 'hello';
    })();
    read.next().value // "hello"
    read.next().value // "h"

for…of循环

for...of循环可以自动遍历 Generator 函数时生成的Iterator对象,且此时不再需要调用next方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function *foo() {
yield 1;
yield 2;
yield 3;
yield 4;
yield 5;
return 6;
}
for (let v of foo()) {
console.log(v);
}
// 1 2 3 4 5
上面代码使用for...of循环,依次显示5个yield表达式的值。这里需要注意,一旦next方法的返回对象的done属性为true,for...of循环就会中止,且不包含该返回对象,所以上面代码的return语句返回的6,不包括在for...of循环之中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
除了for...of循环以外,扩展运算符(...)、解构赋值和Array.from方法内部调用的,都是遍历器接口。
function* numbers () {
yield 1
yield 2
return 3
yield 4
}
// 扩展运算符
[...numbers()] // [1, 2]
// Array.from 方法
Array.from(numbers()) // [1, 2]
// 解构赋值
let [x, y] = numbers();
x // 1
y // 2
// for...of 循环
for (let n of numbers()) {
console.log(n)
}
// 1
// 2
Generator.prototype.return

可以传递参数返回给定的值,并且终结遍历Generator函数(done:flase)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 }
-----------------
没有参数的话value为undefined
g.next() // { value: 1, done: false }
g.return() // { value: undefined, done: true }
yield*表达式

用来在一个Generator函数foo里调用另外一个Generator函数bar,如果bar函数有return,那么foo函数里的yield*fn()就提供了上个函数bar的返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function* foo(){
yield 'a'
yield 'b'
}
function* bar(){
yield 'c'
yield* foo()
yield 'd'
}
for(let x of bar()){
console.log(x)
}
//'c' 'a' 'b' 'd'
--------------
function *foo() {
yield 2;
yield 3;
return "foo";
}
function *bar() {
yield 1;
var v = yield *foo();
console.log( "v: " + v );
yield 4;
}
var it = bar()
it.next() // {value: 1, done: false}
it.next() // {value: 2, done: false}
it.next() // {value: 3, done: false}
it.next() // "v: foo" {value: 4, done: false}
it.next() // {value: undefined, done: true}