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