什麼是generator
generator(生成器)是ES6標準引入的新的數據類型。一個generator看上去像一個函數,但可以返回多次
generator定義
先複習函數的概念,一個函數是一段完整的代碼,調用一個函數就是傳入參數,然後返回結果
function foo(x) { return x + x; }
var r = foo(1);
函數在執行過程中,如果沒有遇到return語句(函數末尾如果沒有return,就是隱含的return undefined;)控制權則無法交回被調用的代碼
generator跟函數很像,定義如下
function* foo(x) { yield x + 1; yield x + 2; return x + 3; }
generator由function*
定義(注意多出的*
號),並且除了return語句,還可以用yield返回多次
以著名的斐波那契數列爲例,要編寫一個產生斐波那契數列的函數,可以這麼寫
function fib(max) {
var t,a = 0, b = 1, arr = [0, 1];
while (arr.length < max) {
t = a + b; a = b; b = t; arr.push(t);
}
return arr; }
fib(5); // [0, 1, 1, 2, 3]
fib(10); // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
函數只能返回一次,所以必須返回一個Array
但是,如果換成generator,就可以一次返回一個數,不斷返回多次
用 generator改寫如下
function* fib(max) {
var t,a = 0,b = 1, n = 1;
while (n < max) {
yield a; t = a + b; a = b; b = t; n ++; }
return a; }
fib(5); // Generator { }
調用一個generator和調用函數不一樣,fib(5)僅僅是創建了一個generator對象,還沒有去執行它
genertor使用方法
調用generator對象有兩個方法
一是不斷地調用generator對象的next()方法
var f = fib(5);
f.next(); //Object { value: 0, done: false }
f.next(); // Object { value: 1, done: false }
f.next(); //Object { value: 1, done: false }
f.next(); // Object { value: 2, done: false }
f.next(); //Object { value: 3, done: true }
f.next();//Object { value: undefined, done: true }
next()方法會執行generator的代碼,然後,每次遇到yield x;就返回一個對象{value: x, done: false},然後“暫停”,返回的value就是yield的返回值,done表示這個generator是否已經執行結束了
如果有return的話,會在返回最後一個結果的時候done變成true,如果全部用的是yield的話,就會再返回完所有的結果之後,返回 { value: undefined, done: true }
當執行到done爲true時,這個generator對象就已經全部執行完畢,如果再調用就會返回undefined
f.next();//Object { value: undefined, done: true }
簡單的看一下有沒有return的區別
有return
function* fib(max) {
var t,a = 0,b = 1, n = 1;
while (n < max) {
yield a; t = a + b; a = b; b = t; n ++; }
return a; };
var f=fib(2);
f.next();
Object { value: 0, done: false }
f.next();
Object { value: 1, done: true }
f.next();
Object { value: undefined, done: true }
沒有return
function* fib(max) {
var t,a = 0,b = 1, n = 1;
while (n < max) {
yield a; t = a + b; a = b; b = t; n ++; }
yield a; };
var f=fib(2);
f.next();
Object { value: 0, done: false }
f.next()
Object { value: 1, done: false }
f.next()
Object { value: undefined, done: true }
區別就在最後一個結果上,看看就明白了
第二個方法是直接用for … of循環迭代generator對象,這種方式不需要我們自己判斷done
emm我也突然發現,有沒有return在這裏也有區別
有return
function* fib(max) {
var t,a = 0,b = 1, n = 1;
while (n < max) {
yield a; t = a + b; a = b; b = t; n ++; }
return a; };
for (var x of fib(5)) { console.log(x); }
0
1
1
2
沒有return
function* fib(max) {
var t,a = 0,b = 1, n = 1;
while (n < max) {
yield a; t = a + b; a = b; b = t; n ++; }
yield a; };
for (var x of fib(5)) { console.log(x); }
0
1
1
2
3
盲猜這個的原理還是根據done是否爲false,由於第一個有return的最後一個結果done是true,所以只返回了4個,第二個5個結果都是false,就返回了5個
有什麼用
因爲generator可以在執行過程中多次返回,所以它看上去就像一個可以記住執行狀態的函數
而如果我們不用generator來保存狀態,一個可行的方法就是用對象保存
用對象來保存狀態,得這麼寫
var fib = {
a: 0,
b: 1,
n: 0,
max: 5,
next: function () {
var r = this.a,
t = this.a + this.b;
this.a = this.b;
this.b = t;
if (this.n < this.max)
{ this.n ++; return r; }
else
{ return undefined; } } };
用對象的屬性來保存狀態,相當繁瑣
generator還有另一個巨大的好處,就是把異步回調代碼變成“同步”代碼,這一點會在後面的AJAX體現,現在我們的功力還不夠,還需要修煉修煉