怎么存储所有的斐波那契数列呢?

Generator 函数是 ES6 提供的一种异步编程解决方案,和普通的函数相似,但是又有着不同。

下面是常见的用法:

1
2
3
4
5
6
7
8
// 声明
function* helloGenerator() {
yield 'Hello Bao';
yield 'Hello Leo';
return 'Hello Saro';
}
// 使用
var hello = helloGenerator(); // {}

和我们常见的函数相比,关键字 function 后加了 * 字符,并且方法体中使用了 yield 关键字。(函数名称并不一定要以 -Generator 作为后缀,这里只是标记其为生成器函数)。当这个生成器函数“执行”之后,变量 hello 并不是表示返回值 'Hello Saro'。在控制台打印 hello,输出为 {}。这其实表示的是一个指向内部状态的指针对象,也就是一个 遍历器对象

可以使用 hello.next() 将状态指向下一个。

1
2
3
4
hello.next(); // { value: 'Hello Bao', done: false }
hello.next(); // { value: 'Hello Leo', done: false }
hello.next(); // { value: 'Hello Saro', done: true }
hello.next(); // { value: undefined, done: true }

生成器函数执行时,遇到 yield 会暂停执行,使用 next() 可以继续执行,并且输出暂停处的结果。

由此可见,yieldreturn 功能上类似,只不过 yield 可以在 next() 后继续从断点处继续执行,而 return 则退出方法。

值得注意的是:
yield 后的函数或者表达式是惰性执行的,只有 yield 之后的代码段执行的时候,才会进行语法校验。

next 方法的参数

yield 本身是没有返回值的,可以使用 next() 传递进去一个参数,用于代替前一个 yield 的返回值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function* split(num) {
var x = yield num / 2;
return x;
}

var ss = split(100);

// 传参
ss.next(); // { value: 50, done: false }
ss.next(100); // { value: 100, done: true }

// or 不传参
ss.next(); // { value: 50, done: false }
ss.next(); // { value: undefined, done: true }

其他的方法

Generator.prototype.return()

用于中断当前的生成器函数,直接返回。

1
2
3
4
5
6
7
8
9
10
11
12
// 声明
function* helloGenerator() {
yield 'Hello Bao';
yield 'Hello Leo';
return 'Hello Saro';
}
// 使用
var hello = helloGenerator(); // {}

hello.next(); // { value: 'Hello Bao', done: false }
hello.return('return'); // { value: 'return', done: true }
hello.next(); // { value: undefined, done: true }

Generator.prototype.throw()

用于向生成器函数内部对应的 yield,抛入异常,如果生成器函数内部不捕获异常,则会中断整个生成器。

1
2
3
4
5
6
7
8
9
10
11
function* helloGenerator() {
yield 'Hello Bao';
yield 'Hello Leo';
return 'Hello Saro';
}
// 使用
var hello = helloGenerator(); // {}

console.log(hello.next()); // { value: 'Hello Bao', done: false }
console.log(hello.throw('throw')); // 抛出异常,并且返回 throw
console.log(hello.next());
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function* helloGenerator() {
try {
yield 'Hello Bao';
yield 'Hello Leo';
} catch (error) {
console.log('error');
}
return 'Hello Saro';
}
// 使用
var hello = helloGenerator(); // {}

console.log(hello.next()); // { value: 'Hello Bao', done: false }
console.log(hello.throw('throw')); // error
console.log(hello.next()); // { value: 'Hello Saro', done: true }

斐波那契数列的实现

回过头来,看一下开始的那个问题,可以用构造器函数构建一个斐波那契数列:

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
function* fibonacci() {
let prev = 0;
let curr = 1;
let temp = 0;
yield prev;
yield curr;
while(true) {
temp = curr;
prev = temp;
curr = prev + curr;
yield curr;
}
}
// 使用解构赋值优化代码
function* fibonacci() {
let [prev, curr] = [0, 1];
yield prev;
yield curr;
while(true) {
[prev, curr] = [curr, prev + curr];
yield curr;
}
}


var f = fibonacci();
for (let i = 0; i < 10; i++) {
console.log(f.next().value);
}

最后

这里介绍了生成器函数的基本使用,有关更为详细的介绍,以及和 Promise 的混合使用,可以参考阮一峰老师的文章Generator 函数的语法Generator 函数的异步应用