亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 語言 > JavaScript > 正文

詳解JavaScript ES6中的Generator

2024-05-06 16:23:54
字體:
來源:轉載
供稿:網友

這篇文章主要介紹了詳解JavaScript ES6中的Generator,ES6版本的JS帶來了諸多簡潔化的改善,需要的朋友可以參考下

今天討論的新特性讓我非常興奮,因為這個特性是 ES6 中最神奇的特性。

這里的“神奇”意味著什么呢?對于初學者來說,該特性與以往的 JS 完全不同,甚至有些晦澀難懂。從某種意義上說,它完全改變了這門語言的通常行為,這不是“神奇”是什么呢。

不僅如此,該特性還可以簡化程序代碼,將復雜的“回調堆棧”改成直線執行的形式。

我是不是鋪墊的太多了?下面開始深入介紹,你自己去判斷吧。

簡介

什么是 Generator?

看下面代碼:

 

 
  1. function* quips(name) { 
  2. yield "hello " + name + "!"
  3. yield "i hope you are enjoying the blog posts"
  4. if (name.startsWith("X")) { 
  5. yield "it's cool how your name starts with X, " + name; 
  6. yield "see you later!"
  7.  
  8. function* quips(name) { 
  9. yield "hello " + name + "!"
  10. yield "i hope you are enjoying the blog posts"
  11. if (name.startsWith("X")) { 
  12. yield "it's cool how your name starts with X, " + name; 
  13. yield "see you later!"

上面代碼是模仿Talking cat(當下一個非常流行的應用)的一部分,點擊這里試玩,如果你對代碼感到困惑,那就回到這里來看下面的解釋。

這看上去很像一個函數,這被稱為 Generator 函數,它與我們常見的函數有很多共同點,但還可以看到下面兩個差異:

通常的函數以 function 開始,但 Generator 函數以 function* 開始。

在 Generator 函數內部,yield 是一個關鍵字,和 return 有點像。不同點在于,所有函數(包括 Generator 函數)都只能返回一次,而在 Generator 函數中可以 yield 任意次。yield 表達式暫停了 Generator 函數的執行,然后可以從暫停的地方恢復執行。

常見的函數不能暫停執行,而 Generator 函數可以,這就是這兩者最大的區別。

原理

調用 quips() 時發生了什么?

 

 
  1. var iter = quips("jorendorff"); 
  2. [object Generator] 
  3. > iter.next() 
  4. { value: "hello jorendorff!", done: false } 
  5. > iter.next() 
  6. { value: "i hope you are enjoying the blog posts", done: false } 
  7. > iter.next() 
  8. { value: "see you later!", done: false } 
  9. > iter.next() 
  10. { value: undefined, done: true } 
  11.  
  12.  
  13. var iter = quips("jorendorff"); 
  14. [object Generator] 
  15. > iter.next() 
  16. { value: "hello jorendorff!", done: false } 
  17. > iter.next() 
  18. { value: "i hope you are enjoying the blog posts", done: false } 
  19. > iter.next() 
  20. { value: "see you later!", done: false } 
  21. > iter.next() 
  22. { value: undefined, done: true } 

我們對普通函數的行為非常熟悉,函數被調用時就立即執行,直到函數返回或拋出一個異常,這是所有 JS 程序員的第二天性。

Generator 函數的調用方法與普通函數一樣:quips("jorendorff"),但調用一個 Generator 函數時并沒有立即執行,而是返回了一個 Generator 對象(上面代碼中的 iter),這時函數就立即暫停在函數代碼的第一行。

每次調用 Generator 對象的 .next() 方法時,函數就開始執行,直到遇到下一個 yield 表達式為止。

這就是為什么我們每次調用 iter.next() 時都會得到一個不同的字符串,這些都是在函數內部通過 yield 表達式產生的值。

當執行最后一個 iter.next() 時,就到達了 Generator 函數的末尾,所以返回結果的 .done屬性值為 true,并且 .value 屬性值為 undefined。

現在,回到 Talking cat 的 DEMO,嘗試在代碼中添加一些 yield 表達式,看看會發生什么。

從技術層面上講,每當 Generator 函數執行遇到 yield 表達式時,函數的棧幀 — 本地變量,函數參數,臨時值和當前執行的位置,就從堆棧移除,但是 Generator 對象保留了對該棧幀的引用,所以下次調用 .next() 方法時,就可以恢復并繼續執行。

值得提醒的是 Generator 并不是多線程。在支持多線程的語言中,同一時間可以執行多段代碼,并伴隨著執行資源的競爭,執行結果的不確定性和較好的性能。而 Generator 函數并不是這樣,當一個 Generator 函數執行時,它與其調用者都在同一線程中執行,每次執行順序都是確定的,有序的,并且執行順序不會發生改變。與線程不同,Generator 函數可以在內部的 yield 的標志點暫停執行。

通過介紹 Generator 函數的暫停、執行和恢復執行,我們知道了什么是 Generator 函數,那么現在拋出一個問題:Generator 函數到底有什么用呢?

迭代器

通過上篇文章,我們知道迭代器并不是 ES6 的一個內置的類,而只是作為語言的一個擴展點,你可以通過實現 [Symbol.iterator]() 和 .next() 方法來定義一個迭代器。

但是,實現一個接口還是需要寫一些代碼的,下面我們來看看在實際中如何實現一個迭代器,以實現一個 range 迭代器為例,該迭代器只是簡單地從一個數累加到另一個數,有點像 C 語言中的 for (;;) 循環。

 

 
  1. // This should "ding" three times 
  2. for (var value of range(0, 3)) { 
  3. alert("Ding! at floor #" + value); 
  4.  
  5. // This should "ding" three times 
  6. for (var value of range(0, 3)) { 
  7. alert("Ding! at floor #" + value); 

現在有一個解決方案,就是使用 ES6 的類。(如果你對 class 語法還不熟悉,不要緊,我會在將來的文章中介紹。)

 

 
  1. class RangeIterator { 
  2. constructor(start, stop) { 
  3. this.value = start; 
  4. this.stop = stop; 
  5.  
  6. [Symbol.iterator]() { return this; } 
  7.  
  8. next() { 
  9. var value = this.value; 
  10. if (value < this.stop) { 
  11. this.value++; 
  12. return {done: false, value: value}; 
  13. else { 
  14. return {done: true, value: undefined}; 
  15.  
  16. // Return a new iterator that counts up from 'start' to 'stop'. 
  17. function range(start, stop) { 
  18. return new RangeIterator(start, stop); 
  19.  
  20. class RangeIterator { 
  21. constructor(start, stop) { 
  22. this.value = start; 
  23. this.stop = stop; 
  24.  
  25. [Symbol.iterator]() { return this; } 
  26.  
  27. next() { 
  28. var value = this.value; 
  29. if (value < this.stop) { 
  30. this.value++; 
  31. return {done: false, value: value}; 
  32. else { 
  33. return {done: true, value: undefined}; 
  34.  
  35. // Return a new iterator that counts up from 'start' to 'stop'. 
  36. function range(start, stop) { 
  37. return new RangeIterator(start, stop); 

查看該 DEMO。

這種實現方式與 Java 和 Swift 的實現方式類似,看上去還不錯,但還不能說上面代碼就完全正確,代碼沒有任何 Bug?這很難說。我們看不到任何傳統的 for (;;) 循環代碼:迭代器的協議迫使我們將循環拆散了。

在這一點上,你也許會對迭代器不那么熱衷了,它們使用起來很方便,但是實現起來似乎很難。

我們可以引入一種新的實現方式,以使得實現迭代器更加容易。上面介紹的 Generator 可以用在這里嗎?我們來試試:

 

 
  1. function* range(start, stop) { 
  2. for (var i = start; i < stop; i++) 
  3. yield i; 
  4.  
  5. function* range(start, stop) { 
  6. for (var i = start; i < stop; i++) 
  7. yield i; 

查看該 DEMO。

上面這 4 行代碼就可以完全替代之前的那個 23 行的實現,替換掉整個 RangeIterator 類,這是因為 Generator 天生就是迭代器,所有的 Generator 都原生實現了 .next() 和 [Symbol.iterator]() 方法。你只需要實現其中的循環邏輯就夠了。

不使用 Generator 去實現一個迭代器就像被迫寫一個很長很長的郵件一樣,本來簡單的表達出你的意思就可以了,RangeIterator 的實現是冗長和令人費解的,因為它沒有使用循環語法去實現一個循環功能。使用 Generator 才是我們需要掌握的實現方式。

我們可以使用作為迭代器的 Generator 的哪些功能呢?

使任何對象可遍歷 — 編寫一個 Genetator 函數去遍歷 this,每遍歷到一個值就 yield 一下,然后將該 Generator 函數作為要遍歷的對象上的 [Symbol.iterator] 方法的實現。

簡化返回數組的函數 — 假如有一個每次調用時都返回一個數組的函數,比如:

 

 
  1. // Divide the one-dimensional array 'icons' 
  2. // into arrays of length 'rowLength'. 
  3. function splitIntoRows(icons, rowLength) { 
  4. var rows = []; 
  5. for (var i = 0; i < icons.length; i += rowLength) { 
  6. rows.push(icons.slice(i, i + rowLength)); 
  7. return rows; 
  8.  
  9.  
  10. // Divide the one-dimensional array 'icons' 
  11. // into arrays of length 'rowLength'. 
  12. function splitIntoRows(icons, rowLength) { 
  13. var rows = []; 
  14. for (var i = 0; i < icons.length; i += rowLength) { 
  15. rows.push(icons.slice(i, i + rowLength)); 
  16. return rows; 

使用 Generator 可以簡化這類函數:

 

 
  1. function* splitIntoRows(icons, rowLength) { 
  2. for (var i = 0; i < icons.length; i += rowLength) { 
  3. yield icons.slice(i, i + rowLength); 
  4.  
  5. function* splitIntoRows(icons, rowLength) { 
  6. for (var i = 0; i < icons.length; i += rowLength) { 
  7. yield icons.slice(i, i + rowLength); 

這兩者唯一的區別在于,前者在調用時計算出了所有結果并用一個數組返回,后者返回的是一個迭代器,結果是在需要的時候才進行計算,然后一個一個地返回。

無窮大的結果集 — 我們不能構建一個無窮大的數組,但是我們可以返回一個生成無盡序列的 Generator,并且每個調用者都可以從中獲取到任意多個需要的值。

重構復雜的循環 — 你是否想將一個復雜冗長的函數重構為兩個簡單的函數?Generator 是你重構工具箱中一把新的瑞士軍刀。對于一個復雜的循環,我們可以將生成數據集那部分代碼重構為一個 Generator 函數,然后用 for-of 遍歷:for (var data of myNewGenerator(args))。

構建迭代器的工具 — ES6 并沒有提供一個可擴展的庫,來對數據集進行 filter 和 map等操作,但 Generator 可以用幾行代碼就實現這類功能。

例如,假設你需要在 Nodelist 上實現與 Array.prototype.filter 同樣的功能的方法。小菜一碟的事:

 

 
  1. function* filter(test, iterable) { 
  2. for (var item of iterable) { 
  3. if (test(item)) 
  4. yield item; 
  5.  
  6.  
  7. function* filter(test, iterable) { 
  8. for (var item of iterable) { 
  9. if (test(item)) 
  10. yield item; 

所以,Generator 很實用吧?當然,這是實現自定義迭代器最簡單直接的方式,并且,在 ES6 中,迭代器是數據集和循環的新標準。

但,這還不是 Generator 的全部功能。

異步代碼

異步 API 通常都需要一個回調函數,這意味著每次你都需要編寫一個匿名函數來處理異步結果。如果同時處理三個異步事務,我們看到的是三個縮進層次的代碼,而不僅僅是三行代碼。

看下面代碼:

 

 
  1. }).on('close'function () { 
  2. done(undefined, undefined); 
  3. }).on('error'function (error) { 
  4. done(error); 
  5. }); 
  6.  
  7. }).on('close'function () { 
  8. done(undefined, undefined); 
  9. }).on('error'function (error) { 
  10. done(error); 
  11. }); 

異步 API 通常都有錯誤處理的約定,不同的 API 有不同的約定。大多數情況下,錯誤是默認丟棄的,甚至有些將成功也默認丟棄了。

直到現在,這些問題仍是我們處理異步編程必須付出的代價,而且我們也已經接受了異步代碼只是看不來不像同步代碼那樣簡單和友好。

Generator 給我們帶來了希望,我們可以不再采用上面的方式。

Q.async()是一個將 Generator 和 Promise 結合起來處理異步代碼的實驗性嘗試,讓我們的異步代碼類似于相應的同步代碼。

例如:

 

 
  1. // Synchronous code to make some noise. 
  2. function makeNoise() { 
  3. shake(); 
  4. rattle(); 
  5. roll(); 
  6.  
  7. // Asynchronous code to make some noise. 
  8. // Returns a Promise object that becomes resolved 
  9. // when we're done making noise. 
  10. function makeNoise_async() { 
  11. return Q.async(function* () { 
  12. yield shake_async(); 
  13. yield rattle_async(); 
  14. yield roll_async(); 
  15. }); 
  16.  
  17. // Synchronous code to make some noise. 
  18. function makeNoise() { 
  19. shake(); 
  20. rattle(); 
  21. roll(); 
  22.  
  23. // Asynchronous code to make some noise. 
  24. // Returns a Promise object that becomes resolved 
  25. // when we're done making noise. 
  26. function makeNoise_async() { 
  27. return Q.async(function* () { 
  28. yield shake_async(); 
  29. yield rattle_async(); 
  30. yield roll_async(); 
  31. }); 

最大的區別在于,需要在每個異步方法調用的前面添加 yield 關鍵字。

在 Q.async 中,添加一個 if 語句或 try-catch 異常處理,就和在同步代碼中的方式一樣,與其他編寫異步代碼的方式相比,減少了很多學習成本。

Generator 為我們提供了一種更適合人腦思維方式的異步編程模型。但更好的語法也許更有幫助,在 ES7 中,一個基于 Promise 和 Generator 的異步處理函數正在規劃之中,靈感來自 C# 中類似的特性。

兼容性

在服務器端,現在就可以直接在 io.js 中使用 Generator(或者在 NodeJs 中以 --harmony 啟動參數來啟動 Node)。

在瀏覽器端,目前只有 Firefox 27 和 Chrome 39 以上的版本才支持 Generator,如果想直接在 Web 上使用,你可以使用 Babel 或 Google 的 Traceur 將 ES6 代碼轉換為 Web 友好的 ES5 代碼。

一些題外話:JS 版本的 Generator 最早是由 Brendan Eich 實現,他借鑒了 Python Generator的實現,該實現的靈感來自 Icon,早在 2006 年的 Firefox 2.0 就吸納了 Generator。但標準化的道路是坎坷的,一路下來,其語法和行為都發生了很多改變,Firefox 和 Chrome 中的 ES6 Generator 是由 Andy Wingo 實現 ,這項工作是由 Bloomberg 贊助的。

yield;

關于 Generator 還有一些未提及的部分,我們還沒有涉及到 .throw() 和 .return() 方法的使用,.next() 方法的可選參數,還有 yield* 語法。但我認為這篇文章已經夠長了,就像 Generator 一樣,我們也暫停一下,另外找個時間再剩余的部分。

我們已經介紹了 ES6 中兩個非常重要的特性,那么現在可以大膽地說,ES6 將改變我們的生活,看似簡單的特性,卻有極大的用處。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美成人精品在线视频| 91亚洲精品在线观看| 亚洲一区二区三| 国产视频福利一区| 国产一区二区激情| 欧美电影在线免费观看网站| 免费91在线视频| 亚洲一区二区福利| 亚洲第五色综合网| 都市激情亚洲色图| 国产精品爱啪在线线免费观看| 日韩欧美高清在线视频| 91麻豆桃色免费看| 精品一区二区亚洲| 欧美一级高清免费| 久久久久女教师免费一区| 亚洲自拍偷拍网址| 国产精品高潮视频| 人人做人人澡人人爽欧美| 欧美日韩国内自拍| 91精品在线观看视频| 欧美成人精品一区| 国产亚洲欧洲高清一区| 91九色视频在线| 亚洲成人黄色在线观看| 欧美午夜激情在线| 91在线观看免费高清| 久久久最新网址| 国内精品小视频在线观看| 亚洲精品欧美一区二区三区| www.xxxx欧美| 精品日本美女福利在线观看| 欧美黑人国产人伦爽爽爽| 欧美一区二区三区免费视| 日韩欧美中文字幕在线播放| 日本午夜人人精品| 欧美激情综合亚洲一二区| 久久精品国产欧美亚洲人人爽| 亚洲成在人线av| 日韩精品在线观| 亚洲男人天堂2024| 欧美电影免费观看高清| 欧美精品久久久久| 亚洲人成五月天| 亚洲美女精品久久| 久久久久女教师免费一区| 国产日韩欧美中文| 欧美猛男性生活免费| 国产亚洲一区精品| 国产精品视频999| 国产精品视频999| 在线视频欧美日韩精品| 国产91久久婷婷一区二区| 日韩中文字幕在线视频播放| 国产亚洲精品激情久久| 波霸ol色综合久久| 日韩av网站大全| 韩日欧美一区二区| 日韩av中文字幕在线免费观看| 国产99久久久欧美黑人| 欧美一级视频免费在线观看| 一区二区欧美日韩视频| 78m国产成人精品视频| 欧美一区二区三区……| 中文字幕精品www乱入免费视频| 精品美女久久久久久免费| 一区二区欧美日韩视频| 性视频1819p久久| 欧美国产日韩一区二区在线观看| 亚洲色图15p| 日韩在线中文字幕| 国产视频精品xxxx| 九九综合九九综合| 欧美精品videosex极品1| 日韩精品视频在线| 亚洲国产欧美自拍| 成年人精品视频| 正在播放欧美一区| 日韩视频―中文字幕| 久久99国产精品自在自在app| 色婷婷久久av| 青青久久aⅴ北条麻妃| 午夜精品福利视频| 日韩高清人体午夜| 日本精品在线视频| 欧美成人黄色小视频| 日韩性xxxx爱| 亚洲欧美三级伦理| 欧美视频专区一二在线观看| 国产一区二区黄| 日韩美女免费视频| 日韩欧美精品网站| 夜夜嗨av一区二区三区四区| 国产精品一久久香蕉国产线看观看| 九色91av视频| 欧美在线性视频| 久久精品久久久久久国产 免费| 久久亚洲精品小早川怜子66| 国产精品白丝av嫩草影院| 亚洲欧美日韩精品久久奇米色影视| 欧美精品videossex性护士| 亚洲精品一区中文| 日韩在线视频观看| 国精产品一区一区三区有限在线| www.日韩不卡电影av| 在线观看日韩av| 国产激情综合五月久久| 欧美日韩精品在线| 亚洲男人的天堂在线| 日韩精品日韩在线观看| 91精品免费久久久久久久久| 91久久久亚洲精品| 成人激情免费在线| 欧美激情videos| 亚洲 日韩 国产第一| 欧美孕妇毛茸茸xxxx| 日韩精品小视频| 精品日韩视频在线观看| 伦伦影院午夜日韩欧美限制| 国产精品久久久久久五月尺| 欧美激情一区二区久久久| 亚洲人成电影网站色| 久久99青青精品免费观看| 亚洲欧美国产精品久久久久久久| 亚洲人成电影网站色…| 日本精品久久久久久久| 国产精品专区h在线观看| 成人有码在线视频| 日韩国产高清污视频在线观看| 91精品国产自产91精品| 91香蕉亚洲精品| 中文字幕日韩专区| 91久久久在线| 国产午夜精品麻豆| 国产主播精品在线| 亚洲免费中文字幕| 国产69久久精品成人| 国产一区二区三区在线观看网站| 欧美激情国产日韩精品一区18| 国产精品久久999| 北条麻妃一区二区三区中文字幕| 亚洲欧美激情精品一区二区| 精品久久久精品| 精品久久久久久久久久久久久| 久久久久久国产精品美女| 欧美性猛交xxxx乱大交蜜桃| 亚洲精品在线看| 精品亚洲一区二区三区在线观看| 亚洲香蕉伊综合在人在线视看| 亚洲国产精品久久久久秋霞不卡| 日韩一区二区精品视频| 日韩一级裸体免费视频| 一本一本久久a久久精品综合小说| 亚洲国模精品私拍| 色综合天天综合网国产成人网| 日韩电影大片中文字幕| 亚洲区在线播放| 亚洲性日韩精品一区二区| 国产亚洲激情在线| 久久久久久久久电影| 国产精品99久久99久久久二8| 亚洲天堂久久av| 欧美日韩加勒比精品一区| 久久国产精品视频|