摘要:函數(shù)返回的遍歷器對(duì)象,還有一個(gè)方法,可以返回給定的值,并且終結(jié)遍歷函數(shù)。這被稱為表達(dá)式個(gè)人理解主要用作遍歷具有遍歷器接口的對(duì)象或函數(shù)。完整形式函數(shù)的函數(shù)總是返回一個(gè)遍歷器,規(guī)定這個(gè)遍歷器是函數(shù)的實(shí)例,也繼承了函數(shù)的對(duì)象上的方法。
語(yǔ)法上
首先可以把它理解成,Generator 函數(shù)是一個(gè)狀態(tài)機(jī),封裝了多個(gè)內(nèi)部狀態(tài)。執(zhí)行 Generator 函數(shù)會(huì)返回一個(gè)遍歷器對(duì)象,也就是說(shuō),Generator 函數(shù)除了狀態(tài)機(jī),還是一個(gè)遍歷器對(duì)象生成函數(shù)。返回的遍歷器對(duì)象,可以依次遍歷 Generator 函數(shù)內(nèi)部的每一個(gè)狀態(tài)。
形式上Generator 函數(shù)是一個(gè)普通函數(shù),但是有兩個(gè)特征。
一是,function關(guān)鍵字與函數(shù)名之間有一個(gè)星號(hào); 二是,函數(shù)體內(nèi)部使用yield表達(dá)式,定義不同的內(nèi)部狀態(tài)(yield在英語(yǔ)里的意思就是“產(chǎn)出”)。調(diào)用上
Generator 函數(shù)的調(diào)用方法與普通函數(shù)一樣,也是在函數(shù)名后面加上一對(duì)圓括號(hào)。不同的是,調(diào)用 Generator 函數(shù)后,該函數(shù)并不執(zhí)行,返回的也不是函數(shù)運(yùn)行結(jié)果,而是一個(gè)指向內(nèi)部狀態(tài)的指針對(duì)象,也就是上一章介紹的遍歷器對(duì)象(Iterator Object)。我們必須調(diào)用遍歷器對(duì)象的next方法,使得指針移向下一個(gè)狀態(tài)。也就是說(shuō),每次調(diào)用next方法,內(nèi)部指針就從函數(shù)頭部或上一次停下來(lái)的地方開(kāi)始執(zhí)行,直到遇到下一個(gè)yield表達(dá)式(或return語(yǔ)句)為止。換言之,Generator 函數(shù)是分段執(zhí)行的,yield表達(dá)式是暫停執(zhí)行的標(biāo)記,而next方法可以恢復(fù)執(zhí)行
function* helloWorldGenerator() { yield "hello"; yield "world"; return "ending"; } var hw = helloWorldGenerator(); hw.next() // { value: "hello", done: false } hw.next() // { value: "world", done: false } hw.next() // { value: "ending", done: true } hw.next() // { value: undefined, done: true }
調(diào)用 Generator 函數(shù),返回一個(gè)遍歷器對(duì)象,代表 Generator 函數(shù)的內(nèi)部指針。以后,每次調(diào)用遍歷器對(duì)象的next方法,就會(huì)返回一個(gè)有著value和done兩個(gè)屬性的對(duì)象。value屬性表示當(dāng)前的內(nèi)部狀態(tài)的值,是yield表達(dá)式后面那個(gè)表達(dá)式的值;done屬性是一個(gè)布爾值,表示是否遍歷結(jié)束。
yield表達(dá)式yield表達(dá)式與return語(yǔ)句既有相似之處,也有區(qū)別。相似之處在于,都能返回緊跟在語(yǔ)句后面的那個(gè)表達(dá)式的值。區(qū)別在于每次遇到y(tǒng)ield,函數(shù)暫停執(zhí)行,下一次再?gòu)脑撐恢美^續(xù)向后執(zhí)行,而return語(yǔ)句不具備位置記憶的功能。一個(gè)函數(shù)里面,只能執(zhí)行一次(或者說(shuō)一個(gè))return語(yǔ)句,但是可以執(zhí)行多次(或者說(shuō)多個(gè))yield表達(dá)式。正常函數(shù)只能返回一個(gè)值,因?yàn)橹荒軋?zhí)行一次return;Generator 函數(shù)可以返回一系列的值,因?yàn)榭梢杂腥我舛鄠€(gè)yield。從另一個(gè)角度看,也可以說(shuō) Generator 生成了一系列的值,這也就是它的名稱的來(lái)歷(英語(yǔ)中,generator 這個(gè)詞是“生成器”的意思)。
語(yǔ)法注意點(diǎn):
1.yield表達(dá)式只能用在 Generator 函數(shù)里面
2.yield表達(dá)式如果用在另一個(gè)表達(dá)式之中,必須放在圓括號(hào)里面
3.yield表達(dá)式用作函數(shù)參數(shù)或放在賦值表達(dá)式的右邊,可以不加括號(hào)。
例如:
function* demo() { foo(yield "a", yield "b"); // OK let input = yield; // OK }next 方法的參數(shù)
yield表達(dá)式本身沒(méi)有返回值(就是說(shuō)let a=yield ;會(huì)返回undefined),或者說(shuō)總是返回undefined。next方法可以帶一個(gè)參數(shù),該參數(shù)就會(huì)被當(dāng)作上一個(gè)yield表達(dá)式的返回值 (注意,是整個(gè)表達(dá)式的返回值而不只是yield 后方的值,例如 let a=yield.......... 參數(shù)會(huì)是a 的值并且會(huì)覆蓋表達(dá)式之前的值)。
function* f() { for(var i = 0; true; i++) { var reset = yield i; console.log(reset); if(reset) { i = -1; } } } var g = f(); g.next()
由于next方法的參數(shù)表示上一個(gè)yield表達(dá)式的返回值,所以在第一次使用next方法時(shí),傳遞參數(shù)是無(wú)效的。V8 引擎直接忽略第一次使用next方法時(shí)的參數(shù),只有從第二次使用next方法開(kāi)始,參數(shù)才是有效的。從語(yǔ)義上講,第一個(gè)next方法用來(lái)啟動(dòng)遍歷器對(duì)象,所以不用帶有參數(shù)。
Generator 函數(shù)返回的遍歷器對(duì)象,都有一個(gè)throw方法,可以在函數(shù)體外拋出錯(cuò)誤,然后在 Generator 函數(shù)體內(nèi)捕獲。
var g = function* () { try { yield; } catch (e) { console.log("內(nèi)部捕獲到錯(cuò)誤", e); } }; var i = g(); i.next(); //外部拋出錯(cuò)誤: i.throw("a");
注意點(diǎn):
1.throw方法拋出的錯(cuò)誤要被內(nèi)部捕獲,前提是必須至少執(zhí)行過(guò)一次next方法。
2.throw方法被捕獲以后,會(huì)附帶執(zhí)行下一條yield表達(dá)式。也就是說(shuō),會(huì)附帶執(zhí)行一次next方法。
3.Generator 函數(shù)體外拋出的錯(cuò)誤,可以在函數(shù)體內(nèi)捕獲;反過(guò)來(lái),Generator 函數(shù)體內(nèi)拋出的錯(cuò)誤,也可以被函數(shù)體外的catch捕獲。
4.一旦 Generator 執(zhí)行過(guò)程中拋出錯(cuò)誤,且沒(méi)有被內(nèi)部捕獲,就不會(huì)再執(zhí)行下去了。如果此后還調(diào)用next方法,將返回一個(gè)value屬性等于undefined、done屬性等于true的對(duì)象,即 JavaScript 引擎認(rèn)為這個(gè) Generator 已經(jīng)運(yùn)行結(jié)束了。
Generator 函數(shù)返回的遍歷器對(duì)象,還有一個(gè)return方法,可以返回給定的值,并且終結(jié)遍歷 Generator 函數(shù)。
yield* 表達(dá)式語(yǔ)法角度看,如果yield表達(dá)式后面跟的是一個(gè)遍歷器對(duì)象,需要在yield表達(dá)式后面加上星號(hào),表明它返回的是一個(gè)遍歷器對(duì)象。這被稱為yield表達(dá)式(個(gè)人理解yield 主要用作遍歷具有遍歷器(Iterator)接口的對(duì)象或函數(shù))。
如:
用來(lái)在一個(gè) Generator 函數(shù)里面執(zhí)行另一個(gè) Generator 函數(shù)。
function* foo() { yield "a"; yield "b"; } // 直接調(diào)用沒(méi)有效果 function* bar() { yield "x"; foo(); yield "y"; } // 使用yield* foo(); function* bar() { yield "x"; yield* foo(); yield "y"; } // 等同于 function* bar() { yield "x"; yield "a"; yield "b"; yield "y"; } // 等同于 function* bar() { yield "x"; for (let v of foo()) { yield v; } yield "y"; } for (let v of bar()){ console.log(v); }
如果被代理的 Generator 函數(shù)有return語(yǔ)句,那么就可以向代理它的 Generator 函數(shù)返回?cái)?shù)據(jù)。
function* foo() { yield 2; yield 3; return "foo"; } function* bar() { yield 1; var v = yield* foo(); console.log("v: " + v); yield 4; } var it = bar(); it.next() // {value: 1, done: false} it.next() // {value: 2, done: false} it.next() // {value: 3, done: false} it.next(); // "v: foo" // {value: 4, done: false} it.next() // {value: undefined, done: true}作為對(duì)象屬性的 Generator 函數(shù)
如果一個(gè)對(duì)象的屬性是 Generator 函數(shù),可以簡(jiǎn)寫(xiě)成下面的形式。
let obj = { * myGeneratorMethod() { ··· } };
完整形式
let obj = { myGeneratorMethod: function* () { // ··· } };Generator 函數(shù)的this
Generator 函數(shù)總是返回一個(gè)遍歷器,ES6 規(guī)定這個(gè)遍歷器是 Generator 函數(shù)的實(shí)例,也繼承了 Generator 函數(shù)的prototype對(duì)象上的方法。
function* g() {} g.prototype.hello = function () { return "hi!"; }; let obj = g(); obj instanceof g // true obj.hello() // "hi!"
上面代碼表明,Generator 函數(shù)g返回的遍歷器obj,是g的實(shí)例,而且繼承了g.prototype。但是,如果把g當(dāng)作普通的構(gòu)造函數(shù),并不會(huì)生效,因?yàn)間返回的總是遍歷器對(duì)象,而不是this對(duì)象。
function* g() { this.a = 11; } let obj = g(); obj.next(); obj.a // undefined
Generator 函數(shù)也不能直接跟new命令一起用
function* F() { yield this.x = 2; yield this.y = 3; } new F()
變通方法
function* gen() { this.a = 1; yield this.b = 2; yield this.c = 3; } function F() { return gen.call(gen.prototype); } var f = new F(); f.next(); // Object {value: 2, done: false} f.next(); // Object {value: 3, done: false} f.next(); // Object {value: undefined, done: true} f.a // 1 f.b // 2 f.c // 3
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/103277.html
摘要:以下展示它是如何工作的函數(shù)使用構(gòu)造函數(shù)創(chuàng)建一個(gè)新的對(duì)象,并立即將其返回給調(diào)用者。在傳遞給構(gòu)造函數(shù)的函數(shù)中,我們確保傳遞給,這是一個(gè)特殊的回調(diào)函數(shù)。 本系列文章為《Node.js Design Patterns Second Edition》的原文翻譯和讀書(shū)筆記,在GitHub連載更新,同步翻譯版鏈接。 歡迎關(guān)注我的專欄,之后的博文將在專欄同步: Encounter的掘金專欄 知乎專欄...
摘要:如果我們只有一個(gè)異步操作,用回調(diào)函數(shù)來(lái)處理是完全沒(méi)有任何問(wèn)題的。事件監(jiān)聽(tīng)使用事件監(jiān)聽(tīng)的方式番禺廣州上述代碼需要實(shí)現(xiàn)一個(gè)事件監(jiān)聽(tīng)器。只處理對(duì)象廣州番禺函數(shù)將函數(shù)的自動(dòng)執(zhí)行器,改在語(yǔ)言層面提供,不暴露給用戶。 概論 由于 JavaScript 是一門單線程執(zhí)行的語(yǔ)言,所以在我們處理耗時(shí)較長(zhǎng)的任務(wù)時(shí),異步編程就顯得尤為重要。js 處理異步操作最傳統(tǒng)的方式是回調(diào)函數(shù),基本上所有的異步操作都可以...
摘要:當(dāng)?shù)鬟\(yùn)行后,會(huì)返回第一次運(yùn)行到或者時(shí)的返回值以格式進(jìn)行返回。而現(xiàn)在了后面的方法必須是總結(jié)總結(jié)一下異步代碼的發(fā)展過(guò)程回調(diào)函數(shù)最基本的解決方法,將異步結(jié)束函數(shù)以參數(shù)的方式傳遞到異步函數(shù)中,也就是使用回調(diào)函數(shù)的方式來(lái)實(shí)現(xiàn)異步邏輯。 介紹 寫(xiě)過(guò)JS代碼的同學(xué)應(yīng)該都知道,JS是單線程的,當(dāng)出現(xiàn)異步邏輯時(shí),就需要使用一些技巧來(lái)實(shí)現(xiàn)。最常見(jiàn)的方法就是使用回調(diào)方法。 回調(diào)方法 比如我們要實(shí)現(xiàn)一個(gè)功...
摘要:當(dāng)這個(gè)迭代器的方法被首次后續(xù)調(diào)用時(shí),其內(nèi)的語(yǔ)句會(huì)執(zhí)行到第一個(gè)后續(xù)出現(xiàn)的位置為止,后緊跟迭代器要返回的值。在這個(gè)回調(diào)函數(shù)里,我們使用第一個(gè)請(qǐng)求返回的,再次發(fā)起一個(gè)請(qǐng)求。 寫(xiě)在前面 本文首發(fā)于公眾號(hào):符合預(yù)期的CoyPan 后續(xù)文章:【JS基礎(chǔ)】從JavaScript中的for...of說(shuō)起(下) - async和await 先來(lái)看一段很常見(jiàn)的代碼: const arr = [1, 2, ...
摘要:傳統(tǒng)的異步方法回調(diào)函數(shù)事件監(jiān)聽(tīng)發(fā)布訂閱之前寫(xiě)過(guò)一篇關(guān)于的文章,里邊寫(xiě)過(guò)關(guān)于異步的一些概念。內(nèi)部函數(shù)就是的回調(diào)函數(shù),函數(shù)首先把函數(shù)的指針指向函數(shù)的下一步方法,如果沒(méi)有,就把函數(shù)傳給函數(shù)屬性,否則直接退出。 Generator函數(shù)與異步編程 因?yàn)閖s是單線程語(yǔ)言,所以需要異步編程的存在,要不效率太低會(huì)卡死。 傳統(tǒng)的異步方法 回調(diào)函數(shù) 事件監(jiān)聽(tīng) 發(fā)布/訂閱 Promise 之前寫(xiě)過(guò)一篇關(guān)...
閱讀 3189·2021-09-28 09:36
閱讀 3723·2021-09-08 09:45
閱讀 1865·2021-09-01 10:43
閱讀 3511·2019-08-30 12:44
閱讀 3382·2019-08-29 17:25
閱讀 1398·2019-08-29 11:03
閱讀 2017·2019-08-26 13:36
閱讀 723·2019-08-23 18:24