這篇文章主要介紹了ES6中Generator函數(shù)的使用方法,具有一定借鑒價(jià)值,需要的朋友可以參考下。希望大家閱讀完這篇文章后大有收獲。下面讓小編帶著大家一起了解一下。
一、什么是Generator函數(shù)
Generator函數(shù)是ES6標(biāo)準(zhǔn)中提出的一種異步編程的解決方案。這種函數(shù)與普通函數(shù)大的區(qū)別在于它可以暫停執(zhí)行,又可以從暫停的位置恢復(fù)繼續(xù)執(zhí)行。
從語(yǔ)法上看,Generator函數(shù)就是一個(gè)狀態(tài)機(jī),封裝了許多內(nèi)部狀態(tài)。
從實(shí)質(zhì)上看,Generator函數(shù)就是一個(gè)遍歷器對(duì)象生成器。(關(guān)于遍歷器對(duì)象,可以參考阮一峰老師的這篇文章)Generator函數(shù)返回一個(gè)遍歷器對(duì)象,遍歷這個(gè)對(duì)象,就可以依次得到函數(shù)內(nèi)部的每一個(gè)狀態(tài)。
二、基本語(yǔ)法
1、定義Generator函數(shù)
定義一個(gè)Generator函數(shù)和定義一個(gè)普通函數(shù)的區(qū)別在于:
function關(guān)鍵字和函數(shù)名之間有一個(gè) *(星號(hào))。
函數(shù)內(nèi)部使用yield來定義每一個(gè)函數(shù)內(nèi)部的狀態(tài)。
如果函數(shù)內(nèi)部有return語(yǔ)句,那么他就是函數(shù)內(nèi)部的最后一個(gè)狀態(tài)。
來看一個(gè)簡(jiǎn)單的例子:
// 定義 function* sayHello() { yield 'hello'; yield 'world'; return 'ending'; } // 調(diào)用 // 注意,hw獲取到的值是一個(gè)遍歷器對(duì)象 let g = sayHello();
上面的例子,定義了一個(gè)名為sayHello
的Generator函數(shù),它內(nèi)部有兩個(gè)yield
表達(dá)式和一個(gè)return
表達(dá)式。所以,該函數(shù)內(nèi)部有三個(gè)狀態(tài):hello
,world
和 return
語(yǔ)句(結(jié)束執(zhí)行)。最后,調(diào)用這個(gè)函數(shù),得到一個(gè)遍歷器對(duì)象并賦值給變量g
。
Generator函數(shù)的調(diào)用方法與普通函數(shù)完全一樣,函數(shù)名()
。不同的是:
Generator函數(shù)調(diào)用后不會(huì)立即執(zhí)行,那么,我們?nèi)绾巫屗_始執(zhí)行內(nèi)部的代碼呢?又如何獲取它內(nèi)部的每一個(gè)狀態(tài)呢?此時(shí),我們必須調(diào)用返回的生成器對(duì)象的.next()方法,才能開始代碼的執(zhí)行,并且使得指針移向下一個(gè)狀態(tài)。
以上面的例子為例:
g.next(); // { value: 'hello', done: false } g.next(); // { value: 'world', done: false } g.next(); // { value: 'ending', done: true } g.next(); // { value: undefined, done: true }
上面的代碼中,一共調(diào)用了四次g
這個(gè)遍歷器對(duì)象的.next()
方法。第一次調(diào)用,sayHello
這個(gè)Generator函數(shù)開始執(zhí)行,直到遇到第一個(gè)yield
表達(dá)式就會(huì)暫停執(zhí)行。.next()
方法會(huì)返回一個(gè)對(duì)象,它的value
屬性就是當(dāng)前yield
表達(dá)式的值hello
,done
屬性的值false
,表示遍歷還沒有結(jié)束。
第二次再調(diào)用.next()
,就會(huì)執(zhí)行到第二個(gè)yield
表達(dá)式處,并暫停執(zhí)行,返回對(duì)應(yīng)的對(duì)象。
第三次調(diào)用.next()
,函數(shù)執(zhí)行到最后的return
語(yǔ)句,此時(shí)標(biāo)志著遍歷器對(duì)象g
遍歷結(jié)束,所以返回的對(duì)象中value
屬性值就是return
后面所跟的值ending
,done
屬性值為true
,表示遍歷已經(jīng)結(jié)束。
第四次以及后面在調(diào)用.next()方法,返回的都會(huì)是{value: undefined, done: true }
。
由Generator函數(shù)返回的遍歷器對(duì)象,只有調(diào)用.next()
方法才會(huì)遍歷到下一個(gè)內(nèi)部狀態(tài),所以這其實(shí)是提供了一種可以暫停執(zhí)行的函數(shù),yield
表達(dá)式就是暫停標(biāo)志。
遍歷器對(duì)象的.next()
方法的運(yùn)行邏輯如下。
yield
表達(dá)式,就暫停執(zhí)行后面的操作,并將緊跟在yield
后面的那個(gè)表達(dá)式的值,作為返回的對(duì)象的value
屬性值。.next()
方法時(shí),再繼續(xù)往下執(zhí)行,直到遇到下一個(gè)yield
表達(dá)式。yield
表達(dá)式,就一直運(yùn)行到函數(shù)結(jié)束,直到return
語(yǔ)句為止,并將return
語(yǔ)句后面的表達(dá)式的值,作為返回的對(duì)象的value
屬性值。return
語(yǔ)句,則返回的對(duì)象的value
屬性值為undefined
。值得注意的是:
yield
關(guān)鍵字只能出現(xiàn)在Generator函數(shù)中,出現(xiàn)在別的函數(shù)中會(huì)報(bào)錯(cuò)。// 出現(xiàn)在普通函數(shù)中,報(bào)錯(cuò) (function () { yield 'hello'; })() // forEach不是Generator函數(shù),報(bào)錯(cuò) [1, 2, 3, 4, 5].forEach(val => { yield val });
yield
關(guān)鍵字后面跟的表達(dá)式,是惰性求值的。 只有當(dāng)調(diào)用.next()
方法、內(nèi)部狀態(tài)暫停到當(dāng)前yield
時(shí),才會(huì)計(jì)算其后面跟的表達(dá)式的值。這等于為JavaScript提供了手動(dòng)的“惰性求值”的語(yǔ)法功能。function* step() { yield 'step1'; // 下面的yield后面的表達(dá)式不會(huì)立即求值, // 只有暫停到這一行時(shí),才會(huì)計(jì)算表達(dá)式的值。 yield 'step' + 2; yield 'setp3'; return 'end'; }
yield
表達(dá)式本身是沒有返回值的,或者說它的返回值為undefined
。使用.next()傳參可以為其設(shè)置返回值。(后面會(huì)講到)function* gen() { for (let i = 0; i < 5; i++) { let res = yield; // yield表達(dá)式本身沒有返回值 console.log(res); // undefined } } let g = gen(); g.next(); // {value: 0, done: false} g.next(); // {value: 1, done: false} g.next(); // {value: 2, done: false}
yield與return的異同:
相同點(diǎn):
不同點(diǎn):
前面我們說到過,yield
表達(dá)式自身沒有返回值,或者說返回值永遠(yuǎn)是undefined
。但是,我們可以通過給.next()
方法傳入一個(gè)參數(shù),來設(shè)置上一個(gè)(是上一個(gè))yield
表達(dá)式返回值。
來看一個(gè)例子:
function* conoleNum() { console.log('Started'); console.log(`data: ${yield}`); console.log(`data: ${yield}`); return 'Ending'; } let g = conoleNum(); g.next(); // 控制臺(tái)輸出:'Started' g.next('a'); // 控制臺(tái)輸出:'data: a' // 不傳入?yún)?shù)'a',就會(huì)輸出'data: undefined' g.next('b'); // 控制臺(tái)輸出:'data: b' // 不傳入?yún)?shù)'a',就會(huì)輸出'data: undefined'
上面的例子,需要強(qiáng)調(diào)一個(gè)不易理解的地方。
第一次調(diào)用.next()
,此時(shí)函數(shù)暫停在代碼第三行的yield
表達(dá)式處。記得嗎?yield
會(huì)暫停函數(shù)執(zhí)行,此時(shí)打印它的console.log()
,也就是代碼第三行的console,由于暫停并沒有被執(zhí)行,所以不會(huì)打印出結(jié)果,只輸出了代碼第二行的'Started'。
當(dāng)?shù)诙握{(diào)用.next()
方法時(shí),傳入?yún)?shù)'a'
,函數(shù)暫停在代碼第四行的yield
語(yǔ)句處。此時(shí)參數(shù)'a'
會(huì)被當(dāng)做上一個(gè)yield
表達(dá)式的返回值,也就是代碼第三行的yiled
表達(dá)式的返回值,所以此時(shí)控制臺(tái)輸出'data: a'
。而代碼第四行的console.log()
由于暫停,沒有被輸出。
第三次調(diào)用,同理。所以輸出'data: b'
。
Generator函數(shù)返回的遍歷器對(duì)象,都有一個(gè).throw()
方法,可以在函數(shù)體外拋出錯(cuò)誤,然后在Generator函數(shù)體內(nèi)捕獲。
function* gen() { try { yield; } catch (e) { console.log('內(nèi)部捕獲', e); } }; var g = gen(); // 下面執(zhí)行一次.next() // 是為了讓gen函數(shù)體執(zhí)行進(jìn)入try語(yǔ)句中的yield處 // 這樣拋出錯(cuò)誤,gen函數(shù)內(nèi)部的catch語(yǔ)句才能捕獲錯(cuò)誤 g.next(); try { g.throw('a'); g.throw('b'); } catch (e) { console.log('外部捕獲', e); }
上面例子中,遍歷器對(duì)象g
在gen
函數(shù)體外連續(xù)拋出兩個(gè)錯(cuò)誤。第一個(gè)錯(cuò)誤被gen
函數(shù)體內(nèi)的catch
語(yǔ)句捕獲。g
第二次拋出錯(cuò)誤,由于gen
函數(shù)內(nèi)部的catch
語(yǔ)句已經(jīng)執(zhí)行過了,不會(huì)再捕捉到這個(gè)錯(cuò)誤了,所以這個(gè)錯(cuò)誤就會(huì)被拋出gen
函數(shù)體,被函數(shù)體外的catch
語(yǔ)句捕獲。
值得注意的是:
try...catch
代碼塊,那么遍歷器對(duì)象的throw
方法拋出的錯(cuò)誤,將被外部try...catch
代碼塊捕獲。try...catch
代碼塊,那么程序?qū)?bào)錯(cuò),直接中斷執(zhí)行。遍歷器對(duì)象的throw
方法被捕獲以后,會(huì)附帶執(zhí)行一次.next()
方法,代碼執(zhí)行會(huì)暫停到下一條yield
表達(dá)式處??聪旅孢@個(gè)例子:
function* gen(){ try { yield console.log('a'); } catch (e) { console.log(e); // 'Error' } yield console.log('b'); yield console.log('c'); } var g = gen(); g.next(); // 控制臺(tái)輸出:'a' g.throw('Error'); // 控制臺(tái)輸出:'b' // throw的錯(cuò)誤被內(nèi)部catch語(yǔ)句捕獲, // 會(huì)自動(dòng)在執(zhí)行一次g.next() g.next(); // 控制臺(tái)輸出:'c'
Generator函數(shù)返回的遍歷器對(duì)象,還有一個(gè).return()
方法,可以返回給定的值,并且直接結(jié)束對(duì)遍歷器對(duì)象的遍歷。
function* gen() { yield 1; yield 2; yield 3; } var g = gen(); g.next(); // { value: 1, done: false } // 提前結(jié)束對(duì)g的遍歷。盡管yield還沒有執(zhí)行完 // 此時(shí)done屬性值為true,說明遍歷結(jié)束 g.return('foo'); // { value: "foo", done: true } g.next(); // { value: undefined, done: true }
如果.return()
方法調(diào)用時(shí),不提供參數(shù),則返回值的value
屬性為undefined
。
yield*
用來在一個(gè)Generator函數(shù)里面執(zhí)行另一個(gè)Generator函數(shù)。
如果在一個(gè)Generator函數(shù)內(nèi)部,直接調(diào)用另一個(gè)Generator函數(shù),默認(rèn)情況下是沒有效果的。
function* gen1() { yield 'a'; yield 'b'; } function* gen2() { yield 'x'; // 直接調(diào)用gen1() gen1(); yield 'y'; } // 遍歷器對(duì)象可以使用for...of遍歷所有狀態(tài) for (let v of gen2()){ 只輸出了gen1的狀態(tài) console.log(v); // 'x' 'y' }
上面的例子中,gen1
和gen2
都是Generator函數(shù),在gen2
里面直接調(diào)用gen1
,是不會(huì)有效果的。
這個(gè)就需要用到 yield*
表達(dá)式。
function* gen1() { yield 'a'; yield 'b'; } function* gen2() { yield 'x'; // 用 yield* 調(diào)用gen1() yield* gen1(); yield 'y'; } for (let v of gen2()){ 輸出了gen1、gen2的狀態(tài) console.log(v); // 'x' 'a' 'b' 'y' }
感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享ES6中Generator函數(shù)的使用方法內(nèi)容對(duì)大家有幫助,同時(shí)也希望大家多多支持創(chuàng)新互聯(lián),關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,遇到問題就找創(chuàng)新互聯(lián),詳細(xì)的解決方法等著你來學(xué)習(xí)!
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。
分享名稱:ES6中Generator函數(shù)的使用方法-創(chuàng)新互聯(lián)
網(wǎng)站URL:http://jinyejixie.com/article6/cechog.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供營(yíng)銷型網(wǎng)站建設(shè)、手機(jī)網(wǎng)站建設(shè)、定制網(wǎng)站、自適應(yīng)網(wǎng)站、網(wǎng)站設(shè)計(jì)、小程序開發(fā)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容