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