JavaScript Generator 函式

admin發表於2019-04-24

本文將通過簡單程式碼例項詳細介紹一下Generator 函式的用法。

看到文章的標題,估計不少朋友會誤認為它只是一個名稱是Generator的普通函式。

事實並非如此,它並不是一個具體的函式,而是一個全新的非同步程式設計方案,一種新的語法結構。

一.語法分析:

此函式是ES2015新增,是一種全新的非同步程式設計方案,既然叫做函式,那肯定具有函式的一些特點。

Generator函式的特點分析如下:

(1).通過function關鍵字宣告。

(2).function關鍵字與函式名稱之間有一個星號(*)。

(3).函式同樣使用大括號進行包裹。

(4).函式體內允許有yield語句,在普通函式中會報錯。

(5).呼叫函式後,其內部的程式碼並不會理解執行,而是返回一個遍歷器物件。

上面總結了Generator函式的主要特點,都是理論性的闡述,比較抽象,下面通過程式碼例項進行演示。

二.例項分析:

[JavaScript] 純文字檢視 複製程式碼
function* antzone() {
  yield "螞蟻部落一";
  yield "螞蟻部落二";
  yield "螞蟻部落三";
  return "return語句"
}
let g = antzone();

上面是一段簡單的關於Generator 函式的程式碼例項,下面進行一下分析:

(1).通過function宣告一個名稱為antzone的Generator函式,但是它之間有一個星號。

(2).星號的位置比較靈活,比如可以緊挨著function關鍵字或者緊挨著函式名稱,甚至之間沒有空格都可以。

(3).函式體內具有三條yield語句,它的具體作用後面會有詳細介紹。

(4).最後是return 語句,但是函式呼叫後返回值並不是return 後面的值。

(5).函式返回一個指向內部狀態的指標物件,相當於遍歷器物件。

Generator函式通過yield語句定義一個狀態,return語句也可以定義一個狀態(與普通函式不同)。

上述程式碼中,三個yield語句和一個return語句定義了四個狀態,也就是此函式封裝四個狀態,適合非同步操作。

三.yield語句:

在前文已經介紹,每一條yield定義一個狀態,可以通過遍歷器物件的next方法訪問每一個狀態。

每訪問一個狀態,都要呼叫一次next方法,可以認為yield語句是暫停識別符號,遇到就得暫停執行。

程式碼例項如下:

[JavaScript] 純文字檢視 複製程式碼執行程式碼
function* antzone() {
  yield "螞蟻部落一";
  yield "螞蟻部落二";
  yield "螞蟻部落三";
  return "return語句"
}
let g = antzone();
console.log(g.next());
console.log(g.next());
console.log(g.next());
console.log(g.next());
console.log(g.next());

程式碼執行效果截圖如下:

a:3:{s:3:\"pic\";s:43:\"portal/201904/24/144640r4t11v3h7vbf044t.png\";s:5:\"thumb\";s:0:\"\";s:6:\"remote\";N;}

程式碼分析如下:

(1).Generator函式中封裝了四個狀態,呼叫函式後返回一個遍歷器物件。

(2).注意呼叫函式後,只是返回一個遍歷器物件,其內部的程式碼並未執行。

(3).在next方法呼叫之前,指標指向函式體內程式碼開始的位置。

(4).第一次呼叫next方法,程式碼從開始位置執行,一直到碰到第一個yield語句,然後返回一個物件,物件具有value和done兩個屬性,value屬性值是當前yield後面表示式的返回值,done的屬性值是一個布林值,如果true表示對yield遍歷介紹,false表示沒結束,很明顯當前沒有結束。

(5).第二個呼叫next方法,程式碼接著執行,直到碰到第二個yield語句,然後返回一個物件,物件value屬性值是螞蟻部落二,done屬性值為false,一直持續下去。

(6).return語句也是一個狀態,與對待yield語句相同,由於它是最後一個了,所以的done屬性值為true。

(7).再執行完return之後,又一次呼叫next方法,因為後面已經沒有yield或者return語句了,可以看到返回物件的value屬性值是undefined,done的屬性值為true。

yield注意細節:

此語句不能用於普通函式,否則會報錯。

a:3:{s:3:\"pic\";s:43:\"portal/201904/24/144702fvuma4gaxnncaavv.png\";s:5:\"thumb\";s:0:\"\";s:6:\"remote\";N;}

如果yield語句位於其他語句中,那麼最好使用小括號包裹:

a:3:{s:3:\"pic\";s:43:\"portal/201904/24/144723gkg4eq2n47a64joo.png\";s:5:\"thumb\";s:0:\"\";s:6:\"remote\";N;}

可以看到上面程式碼報錯了,程式碼修改如下:

[JavaScript] 純文字檢視 複製程式碼
function *antzone() {
  console.log("歡迎來到" + (yield "螞蟻部落"));
}

上述程式碼就是正確的,只要將yield語句用小括號包裹起來即可。

但是當此語句作為函式的引數或者在賦值語句中,不使用小括號包裹也是可以的。

[JavaScript] 純文字檢視 複製程式碼
func(yield "螞蟻部落");
let antzone = yield;

上面程式碼中,雖然yield語句沒有被小括號包裹,但是並不會報錯。

四.next()方法:

首先,再理一下前面一些相關知識:

(1).呼叫next方法可以返回一個具有兩個屬性的物件。

(2).yield後面的表示式有返回值,但是yield語句本身沒有返回值。

next方法可以接受一個引數,此引數會作為上一個yield語句的返回值。

程式碼例項如下:

[JavaScript] 純文字檢視 複製程式碼執行程式碼
function* antzone() {
  let x=yield "螞蟻部落";
  console.log(x==undefined);
}
let g=antzone();
g.next();
g.next();

程式碼執行效果截圖如下:

a:3:{s:3:\"pic\";s:43:\"portal/201904/24/144841h1flpd132q2p2trt.png\";s:5:\"thumb\";s:0:\"\";s:6:\"remote\";N;}

上面的截圖證明了這一點,yield語句沒有返回值或者說返回值是undefined。

再通過一段程式碼例項對next方法的引數進行一下演示:

[JavaScript] 純文字檢視 複製程式碼執行程式碼
function* antzone(num) {
  let x = 2 * (yield num);
  let y = yield x*3;
  console.log(x,y);
}
let g=antzone(5);
console.log(g.next());
console.log(g.next(2));
console.log(g.next(3));

程式碼執行效果截圖如下:

a:3:{s:3:\"pic\";s:43:\"portal/201904/24/144909r6uof81u7ta2dfp1.png\";s:5:\"thumb\";s:0:\"\";s:6:\"remote\";N;}

程式碼分析如下:

(1).呼叫antozne函式,並傳遞引數5,後面會用到。

(2).第一次呼叫next方法,並沒有傳遞任何引數,其實傳遞了也沒用,因為它前面沒有yield語句。

(3).第二次呼叫next方法,傳遞引數為數字2,那麼第一個yield語句的返回值就被設定為2。

(4).於是,x的值等於2*(2),也就是等於4。

(5).第三次呼叫next方法,傳遞引數為數字3,那麼第二個yield語句的返回值就被設定為3。

(6).於是,y的值等於3,所以console.log(x,y)列印結果就是4,3。

考慮到文章的篇幅問題,關於Generator函式的其他知識本文不再做詳細介紹。

更多內容可以參閱如下幾篇文章:

Generator.prototype.throw() 方法一章節。

Generator.prototype.return() 方法一章節。

yield* 表示式一章節。

Generator函式 this一章節。

Generator函式推導(ES2016)一章節。

相關文章