摘要:調用函數后和普通函數不同的是,該函數并不立即執行,也不返回函數執行結果,而是返回一個指向內部狀態的對象,也可以看作是一個遍歷器對象。第一個只是用來啟動函數內部的遍歷器,傳參也沒有多大意義。
之前斷斷續續接觸到了一些ES6的知識,異步編程方面聽得比較多的就是Promise,直到最近比較系統地學習了ES6的新特性才發現Generator這個神奇的存在,它可以實現一些前所未有的事情,讓我頓時對它充滿了興趣。
為什么需要Generator?JavaScript異步編程是為解決JavaScript執行環境是“單線程”這個問題的。在JavaScript中,異步編程的使用非常頻繁,也經常會出現需要逐步完成多個異步操作的情況。之前用回調函數實現異步編程如果碰到了這種問題就需要嵌套使用回調函數,異步操作越多,嵌套得就越深,這樣非常不利于代碼的維護,代碼閱讀起來也很困難。Generator函數是ES6提出的一種異步編程解決方案,它可以避免回調的嵌套,但是它的用處可不僅僅如此哦,待我細細道來。
舉個小例子function* gen1() { yield 1; yield "hello"; return true; } let g1 = gen1(); g1.next(); // Object {value: 1, done: false} g1.next(); // Object {value: "hello", done: false} g1.next(); // Object {value: true, done: true} g1.next(); // Object {value: undefined, done: true}
上面的代碼就定義了一個Generator函數,Generator函數的定義跟普通函數差不多,只是在function關鍵字后面加了一個星號。調用Generator函數后和普通函數不同的是,該函數并不立即執行,也不返回函數執行結果,而是返回一個指向內部狀態的generator對象,也可以看作是一個遍歷器對象。然后必須調用該對象的next方法,讓函數繼續走下去,是指針移向下一個狀態。每當碰到yield語句,內部指針就停下來,直到下一次調用next()才開始執行。
上面代碼調用了四次next方法,遍歷才結束。next方法會返回一個有兩個屬性的對象,value屬性的值為當前yield語句的值,done屬性的值表示遍歷是否結束,即最后一次調用next方法時,再也碰不到yield或者return語句了。
星號寫在哪:
function關鍵字和函數名之間的星號寫在哪都可以,只要在兩者之間即可,但是一般都采取我上面代碼的那種寫法。
上面說了那么多,想必大家已經知道Generator函數是怎么用的了,那么Generator本質上到底是個啥呢?Generator函數的理解有多種:
Generator函數可以被理解成一個狀態機,里面封裝了多種狀態,有興趣的同學可以去了解一下狀態機,操作系統的書里都會講到。
Generator函數還可以被理解成一個遍歷器對象生成器,它返回的遍歷器對象可以依次遍歷Generator函數內部的每一個狀態。這就是為什么之前說Generator函數不僅是為了解決回調函數嵌套問題。Generator函數是生成一個對象,但是調用的時候前面不能加new命令。
yield語句yield語句是Generator函數內部可以暫停執行程序的語句,yield語句后面的值可以是各種數據類型,字符串,整數,布爾值等等都可以。這里主要想說說Generator函數中yield語句和return語句的區別。
和return語句區別從上面的例子可以看出,函數不僅是碰到yield語句才會停止執行,碰到return語句也會停止執行。這很容易理解,不管怎樣Generator函數也是一個函數,碰到return語句必然會停止執行,返回值。那么,兩者的區別是什么呢?先來看個例子:
function* gen2() { return true; yield 1; yield "hello"; } let g2 = gen2(); g2.next(); // Object {value: true, done: true} g2.next(); // Object {value: undefined, done: true}
從上面例子可以看出,當碰到return語句時,返回對象的done屬性值就為true,遍歷結束,不管后面是否還有yield或者return語句。這種區別本質上是因為yield語句具備位置記憶功能而return語句則沒有該功能。
再說一點Generator函數,不管內部有沒有yield語句,調用函數時都不會執行任何語句,只有當調用next(),內部語句才會執行,只要調用next(),就會返回一個對象。yield語句只是函數暫停執行的一個標記。
function* gen3() { console.log("執行了么?"); } let g3 = gen3(); // 沒有任何輸出 g3.next(); // 執行了么? // Object {value: undefined, done: true}
注意:yield函數不能在普通函數中使用,否則會報錯。
next方法除了yield語句,next方法也是Generator函數實現中很重要的特性。既然next()是一個函數,那么這個函數可以帶參數么,當然可以。上面的例子比較簡單,都只是一些單純的yield語句,其實Generator函數和普通函數一樣里面是可以進行各種復雜的計算和操作的,也可以有各種循環語句,不僅next方法可以傳參數,Generator函數也是可以傳參數的,立馬上例子:
function* gen4(a) { let b = yield (a + 1); return b * 2; } let g4 = gen4(1); g4.next(); // Object {value: 2, done: false} g4.next(); // Object {value: NaN, done: true} let g5 = gen4(1); g5.next(); // Object {value: 2, done: false} g5.next(3); // Object {value: 6, done: true}
上面例子中,Generator函數需要接收一個參數a,表面上變量b是用yield語句賦值了,但是遺憾的是這個賦值好像并沒有成功,當第二次調用next方法(沒有傳參數)時,返回的對象value值居然為NaN,而不是我們想的 2 *(1+1)= 4。但是如果第二次調用next方法時,傳入一個參數3,返回對象的value值就為6。這可以說明兩點:
yield語句沒有返回值,或者總是返回undefined;
next方法如果帶上一個參數,這個參數就是作為上一個yield語句的返回值。
注意:因為next方法表示上一個yield語句的返回值,所以必須有上一個yield語句的存在,那么第一次調用next方法時就不能傳參數。第一個next只是用來啟動Generator函數內部的遍歷器,傳參也沒有多大意義。
再說Generator函數與普通函數區別 可以用prototype么?雖然Generator函數和普通函數區別很大,但是Generator函數的實例也可以繼承Generator函數的prototype對象上的方法。
function* gen5() {} gen5.prototype.say = function() { console.log("有generator?"); } let g6 = gen5(); g6.say(); // 有generator?
從上面代碼可以看出,Generator函數返回的g6,繼承了gen5.prototype。
this咋用?大家都知道普通函數都會有一個this對象,那么Generator的this對象怎么用呢?還是例子更直觀:
function* gen6() { this.a = 1; } let g7 = gen6(); g7.a; // undefined
上面代碼中,Generator函數在this對象上添加了一個屬性a,g7實例并不能取到這個屬性。那么怎么讓Generator函數返回一個可以正常使用this對象的實例呢?阮一峰老師提供了一種方法,首先,生成一個空對象,使用call方法綁定Generator函數內部的this。這樣,構造函數調用以后,這個空對象就是Generator函數的實例對象了。參考代碼在這:http://es6.ruanyifeng.com/#docs/generator
Generator函數與IteratorGenerator函數返回的是一個遍歷器對象,那么它在遍歷這方面肯定有用武之地,下一次討論Iterator時候再總結吧。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/88039.html
摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復雜性。異步編程入門的全稱是前端經典面試題從輸入到頁面加載發生了什么這是一篇開發的科普類文章,涉及到優化等多個方面。 TypeScript 入門教程 從 JavaScript 程序員的角度總結思考,循序漸進的理解 TypeScript。 網絡基礎知識之 HTTP 協議 詳細介紹 HTT...
摘要:異步編程解決方案筆記最近讀了樸靈老師的深入淺出中異步編程一章,并參考了一些有趣的文章。另外回調函數中的也失去了意義,這會使我們的程序必須依賴于副作用。 JavaScript 異步編程解決方案筆記 最近讀了樸靈老師的《深入淺出NodeJS》中《異步編程》一章,并參考了一些有趣的文章。在此做個筆記,記錄并鞏固學到的知識。 JavaScript異步編程的兩個核心難點 異步I/O、事件驅動使得...
摘要:廖雪峰的教程學習筆記變量作用域不能聲明塊級的變量,的函數內變量聲明會被提升至函數體開頭則用來解決這個塊級變量聲明,于引入。普通函數一般將賦值為。高階函數輸出結果是。箭頭函數新引入的相當于如下的匿名函數其中為參數。 廖雪峰的JavaScript教程學習筆記 1. 變量作用域 var 不能聲明塊級的變量,js的函數內變量聲明會被提升至函數體開頭let 則用來解決這個塊級變量聲明,于ES6...
摘要:去除數組的重復成員這表明,在內部,兩個是相等。返回一個布爾值,表示該值是否為的成員。使用回調函數遍歷每個成員沒有返回值。對象特點對象有三種狀態進行中已完成,又稱和已失敗。方法是的別名,用于指定發生錯誤時的回調函數。 Set和Map數據結構 Set 新的數據結構Set類似于數組,但是成員的值都是唯一的,沒有重復的值。Set 本身是一個構造函數,用來生成 Set 數據結構。接受一個數組(或...
摘要:變量的解構賦值中允許按照一定模式,從數組和對象中提取,對變量進行賦值。數組的解構賦值上面的代碼標示可以從數組中提取值,按照位置的對應關系對變量進行賦值。默認值解構賦值允許指定默認值。 變量的解構賦值 ES6中允許按照一定模式,從數組和對象中提取,對變量進行賦值。 數組的解構賦值 var [a,b,c] = [1,2,3]; a // 1; b // 2; c // 3; 上面的代碼標示...
閱讀 3054·2023-04-26 00:40
閱讀 2391·2021-09-27 13:47
閱讀 4197·2021-09-07 10:22
閱讀 2966·2021-09-06 15:02
閱讀 3307·2021-09-04 16:45
閱讀 2484·2021-08-11 10:23
閱讀 3599·2021-07-26 23:38
閱讀 2900·2019-08-30 15:54