本節的內容,是建立在iterator遍歷器知識的基礎上。所以希望還沒有看上一節的內容的話,最好還是看一看,當然你如果熟悉iterator就沒有那個必要了.
既然你都看到這裡來了,就咱們就接著往下講…
聲明Generator函數
我們要學習的這個新函數叫做:Generator函數,又稱生成器函數,是ES6的一個重要的新特性。
我們來看看這個函數張什麼模樣:
//聲明一個Hello的Generator函數 function* Hello(name) { yield `hello ${name}`; yield `how are you`; yield `bye`; }
上面這個就是Generator函數,乍一看,是不是跟普通的函數沒什麼兩樣?確實很像,但是我們要知道它有兩個重要的區別:
-
普通函數用function來聲明,Generator函數用function*聲明。
-
Generator函數內部有新的關鍵字:yield,普通函數沒有。
PS:函數體內用到了ES6的新特性:字元串模板。第六節的內容,點擊可以查看。
了解了Generator函數的聲明方式,我們又多了兩個疑問:
-
Generator函數運行起來會發生什麼?
-
****關鍵字yield語句的作用是什麼?
調用Generator函數
帶著這兩個疑問我們往下看,我們試著就調用一下這個名字叫Hello的Generator函數,看看會發生什麼:
//聲明一個Hello的Generator函數 function* Hello(name) { yield `hello ${name}`; yield `how are you`; yield `bye`; } //調用Hello函數 let ite = Hello('前端君'); //結果:[object Generator] ite.next(); //{value: "hello 前端君", done: false} ite.next(); //{value: "how are you", done: false} ite.next(); //{value: "bye", done: false} ite.next(); //{value: undefined, done: true}
看到這裡,估計你也看到了一個熟悉的面孔:next()方法。(上一節iterator遍歷器的內容)。
我們一起看看整個過程發生了什麼:
一開始,我們調用Hello(「前端君」),函數執行後,返回了一個:[object Genrator]生成器對象,我們把它賦值到變數ite中,僅此而已,並沒有做太多的事情。
接著,第1次調用生成器對象ite的next( )方法,返回了一個對象:
{value: "hello 前端君", done: false}
第2次調用生成器對象ite的next( )方法,同樣得到了一個對象:
{value: "how are you", done: false}
第3次調用生成器對象ite的next( )方法,又得到了一個對象:
{value: "bye", done: false}
直到,第4次調用生成器對象ite的next( )方法,返回的對象:
{value: undefined, done: true}
看到這裡有沒有發現,這裡生成器的next( )方法的和遍歷器iterator的next( )方法的返回結果是不是一樣?
沒錯,你可以把Generator函數被調用後得到的生成器理解成一個遍歷器iterator,用於遍歷函數內部的狀態。(所以要求大家先學習第十三節iterator遍歷器的知識)
Generator函數的行為
通過上面的案例,我們知道了:Generator函數被調用後並不會一直執行到最後,它**是先回返回一個生成器對象,然後hold住不動****,等到生成器對象的next( )方法被調用後,函數才會繼續執行,直到遇到關鍵字yield後,又會停止執行,**並返回一個Object對象,然後繼續等待,直到next( )再一次被調用的時候,才會繼續接著往下執行,直到done的值為true。
yield語句的作用
而yield在這裡起到了十分重要的作用,就相當於暫停執行並且返回資訊。有點像傳統函數的return的作用,但不同的是普通函數只能return一次,但是Generator函數可以有很多個yield。而return代表的是終止執行,yield代表的是暫停執行,後續通過調用生成器的next( )方法,可以恢復執行。
next( )方法接收參數
此外,next( )方法還可以接受一個參數,它的參數會作為上一個yield的返回值,我們來看一下:
//聲明一個Hello的Generator函數 function* Hello() { let res = yield `hello`; yield res; } let iterator = Hello(); //結果:一個生成器對象 iterator.next(); //結果:{value: "hello", done: false} iterator.next("前端君"); //結果:{value: "前端君", done: false}
注意函數體內的第一個yield關鍵字,我們把它的返回值賦值給了一個變數res。
再看2次next方法的調用:
第1次調用next( )方法,返回的對象屬性value值為「hello」,屬性done值為:fasle,並暫停執行。
第2次next( )方法,傳入參數:字元串「前端君」。此時,第二個yield關鍵字緊跟著的是變數res,而變數res的值正是上一個關鍵字yield的返回值。也就是說這個值正是我們傳入的參數:「前端君」。因為:next( )的參數會作為上一個yield的返回值。
慢慢體會一下,理清邏輯,稍微有點繞。
關鍵字yield*
在一個Generator函數裡面,如果我們想調用另一個Generator函數,就需要用到的關鍵字是:yield*。
我們來看看怎麼玩,程式碼有點長,但是很好理解:
//聲明Generator函數:gen1 function* gen1() { yield "gen1 start"; yield "gen1 end"; } //聲明Generator函數:gen2 function* gen2() { yield "gen2 start"; yield "gen2 end"; } //聲明Generator函數:start function* start() { yield "start"; yield* gen1(); yield* gen2(); yield "end"; } //調用start函數 var ite = start(); //創建一個生成器 ite.next(); //{value: "start", done: false} ite.next(); //{value: "gen1 start", done: false} ite.next(); //{value: "gen1 end", done: false} ite.next(); //{value: "gen2 start", done: false} ite.next(); //{value: "gen2 end", done: false} ite.next(); //{value: "end", done: false}
我們主要看start( )這個Generator函數,其中有兩句程式碼:
yield* gen1();
yield* gen2();
這裡使用了關鍵字yield*來實現調用另外兩個Generator函數。從後面的多個next( )方法得到的結果看,我們可以知道:
** 如果一個Generator函數A執行過程中,進入(調用)了另一個Generator函數B,那麼會一直等到Generator函數B全部執行完畢後,才會返回Generator函數A繼續執行。**
Generator函數的用途
以上就是對Generator函數的講解介紹,它是ES6的一個很重要的新特性。它可以控制函數的內部狀態,依次遍歷每個狀態;可以根據需要,輕鬆地讓函數暫停執行或者繼續執行。
根據這個特點,我們可以利用Generator函數來實現非同步操作的效果。
原理是:利用Generator函數暫停執行的作用,可以將非同步操作的語句寫到yield後面,通過執行next方法進行回調。
本節小結
總結:Generator函數是一種特殊的函數,可以使用關鍵字yield和next( )實現暫停和繼續執行,而關鍵字yield*專門用於調用Generator函數,看似簡單的特性,在實際開發中卻有極大的用處。
更多前端學習內容文章乾貨請關注我的專欄(不斷更新)
阿里名廠標準web前端高級工程師教程目錄大全,從基礎到進階,看完保證您的薪資上升一個台階
在這裡我給大家準備了很多的學習資料
其實你與阿里工程師的差距只差這些東西


