快速了解 ES6 的生成器

2021-02-14 海人的博客

生成器是 ECMAScript 6 新增的一個極為靈活的結構,擁有一個函數塊內暫停和恢復代碼執行的能力。

生成器函數提供了一個強大的選擇:它允許你定義一個包含自有迭代算法的函數,同時它可以自動維護自己的狀態。

使用 function*語法定義生成器函數,而且 * 不受兩側空格的影響。只要是可以定義函數的地方,就可以定義生成器。

function* generator() {}

注意:箭頭函數不能用來定義生成器函數。

方法

調用生成器函數會產生一個符合可迭代協議和迭代器協議的生成器對象。生成器對象一開始處於暫停執行的狀態。與迭代器相似,生成器也實現了 Iterator 接口。

Generator.prototype.next()

Generator 對象具有 next() 方法,調用這個方法會讓生成器開始或恢復執行,而且返回的是一個 IteratorResult 對象,具有 done 屬性和 value 屬性。默認情況下,返回的值為 { done: true, value: undefined }。

function* generator() {
return 'sample';
}
const v = generator();
// 默認的迭代器是自引用的
console.log(v); // generator {<suspended>}
console.log(generator()[Symbol.iterator]()); // generator {<suspended>}
console.log(v.next()); // { value: 'sample', done: true }

生成器還可以通過 next(value) 方法向內部傳參,並且這個參數會變成 yield 的結果。

function* generator() {
let v = yield 10;
yield v;
}
let v = generator();
console.log(v.next(1)); // { value: 10, done: false }
console.log(v.next(2)); // { value: 2, done: false }

第一次調用 next() 傳入的值不會被使用,因為這一次調用時為了開始執行生成器函數,它會執行並返回第一個 yield 10 的結果。第二次 next() 調用,獲得了 2 作為結果let v = 2 並返回 yield v。

Generator.prototype.return()

Generator 提供的 return() 方法返回給定的值並結束生成器。因此可以使用 return() 方法提前終止生成器。

function* generator() {
yield 'sample';
yield ;
yield 'example';
return 'instance';
}
const v1 = generator();
console.log(v1); // generator {<suspended>}
console.log(v1.return("quit")); // { done: true, value: "quit" }
console.log(v1); // generator {<closed>}

當 return() 方法沒有提供參數,則返回對象的 value 屬性的值為 undefined。

當使用 return() 方法關閉了生成器後就無法恢復,後續調用 next() 方法返回的值為 {done: true, value: undefined}。

console.log(v1.next()); // {done: true, value: undefined}

for-of 循環等內置語言結構會忽略狀態為 done: true 的 IteratorObject 內部返回的值。

function* generator() {
yield 'sample';
yield ;
yield 'example';
return 'instance';
}
let v1 = generator();
for (const x of v1) {
if (x === 'example') {
v1.return("quit");
}
console.log(x);
}
// sample
// undefined
// example

Generator.prototype.throw()

生成器還提供了 throw() 方法用來向生成器中注入一個錯誤。如果內部未處理該錯誤,那麼生成器就會關閉。

function* generator() {
yield 'sample';
yield ;
yield 'example';
return 'instance';
}

const v = generator();
console.log(v); // // generator {<suspended>}
try {
v.throw(new Error("Throw Error"));
} catch (e) {
console.log(e); // Error: Throw Error
}
console.log(v); // generator {<closed>}

但是生成器函數內部處理了這個錯誤,那麼生成器就不會關閉,還可以恢復執行。錯誤處理會跳過對應的 yield,因此在這個例子中會跳過一個值。如下所示:

function* generator() {
for (const x of ["sample", "example", "instance"]) {
try {
yield x;
} catch (e) {
console.log("Error caught!");
}
}
}
const v = generator();
console.log(v.next()); // { value: "sample", done: false}
v.throw(new Error("Throw Error")); //
console.log(v); // generator {<suspended>}
console.log(v.next); // { value: "instance", done: false}

在這裡,向生成器內部注入一個錯誤,該錯誤會被 yield 關鍵字拋出,並且在生成器內部的 try-catch 語句塊中處理了該錯誤。此時,生成器函數還是會繼續執行,但下次調用 next() 方法就不會產生 example 值,而是產生 instance 值。

注意:如果生成器對象還沒有開始執行,那麼調用 throw() 拋出的錯誤不會在函數內部被捕獲,因為這相當於在函數塊外部拋出了錯誤。

yield

ECMAScript 6 提供了 yield 關鍵字用來讓生成器函數暫停,並且函數作用域的狀態會保留。生成器對象會通過調用 next() 方法讓生成器函數恢復執行。yield 省略表達式會返回 undefined:

function* generator() {
yield 'sample';
yield ;
yield 'example';
return 'instance';
}
let v1 = generator();
console.log(v1.next()); // { value: 'sample', done: false }
console.log(v1.next()); // { value: undefined, done: false }
console.log(v1.next()); // { value: 'example', done: false }
console.log(v1.next()); // { value: 'instance', done: true }
console.log(v1.next()); // { value: undefined, done: true }

生成器對象可當成可迭代對象,可以使用 for...of 循環。

function* generator() {
for (const x of ["sample", "example", "instance"]) {
yield x;
}
}
for (const x of generator()) {
console.log(x);
}
// sample
// example
// instance

注意:yield 關鍵字只能在生成器函數內部使用且必須位於生成器函數定義中,出現在嵌套的非生成器函數中會拋出錯誤。

還可以使用 yield* 語法增強 yield 的行為,用於委託給另一個 generator 或可迭代的對象。

function* generator() {
for (const v of [1, 2, 3]) {
yield v;
}
}

等價於

function* generator() {
yield* [1, 2, 3];
}

等價於

function* generator1() {
yield 1;
yield 2;
}
function* generator2() {
yield* generator1();
yield 3;
}

注意,yield 與 * 兩側的空格不影響其行為。

由於 yield* 可以調用另外一個生成器,所以通過 yield* 可以實現遞歸調用。

function* recs(n) {
if (n > 1) {
yield* f(n-1);
}
yield n;
}
for (const x of recs(3)) {
console.log(x);
}
// 1
// 2
// 3

使用遞歸生成器結構和 yield* 可以優雅地表達遞歸算法。

小結

生成器是一種特殊的函數,調用之後會返回一個生成器對象。生成器對象實現了 Iterable 接口,因此應用場景與迭代器一樣。生成器支持 yield 關鍵字,用於暫停執行生成器函數,與 next() 方法搭配產生一系列值。yield* 表達式可以在生成器中調用其它生成器。

相關焦點

  • 快速開發平臺之代碼生成器——實例分享
    上一篇文章我們分享了《如何利用快速開發平臺可視化開發表單?》,今天為大家介紹快速開發平臺的代碼生成器功能。快速開發平臺的代碼生成器已經把常用的開發場景做成開發模板,按照開發嚮導一步步走,在遇到有複雜業務邏輯的地方稍作修改就可以。當然XJR快速開發平臺提供了原始碼,您的開發水平比較高的話可以做深層次的擴展。
  • 最新250+場景生成器素材,一鍵快速生成場景,限時領取
    最新250+場景生成器素材,一鍵快速生成場景,限時領取!(領取方式見文章末尾)今天為夥伴們帶來的是【250+場景生成器素材:Fancy Items V3】,多個預製場景,都是最好的工業設計師的作品。在夥伴們搭建場景的時候可以一鍵生成,快速完工。
  • 在條碼生成器裡快速批量製作條碼
    條碼的製作列印需要藉助條碼生成器,而常見的在線的條碼碼生產器一般都是單個生成的,如果想要批量生成條形碼需要藉助專門的條碼生成器來實現。以Label mx條碼生成器為例,演示一下如何條碼如何快速生成。
  • JAVA代碼生成器,快速開發平臺之魂
    經過對相關產品的一系列研究與開發,直接使用滑鼠操作的可視化代碼生成器便誕生了。當然,初期還只有.NET,但現在JAVA已經正式加入。代碼生成器的意義1.使用代碼生成器,能有效減少編寫代碼的工作量,因為增刪改查的基本代碼直接自動生成,工作量會減少一半以上。2. 代碼更規範,可以減少bug。在經驗較為缺乏的團隊裡,規範的代碼和結構,能夠引導開發者遵守規範。現有的標準代碼也能用於參考,從而減少減少錯誤。3. 集中精力解決業務問題,進一步提高項目開發效率。
  • 二維碼生成器
    版本: 6.13 *使用豌豆莢官方商店能下載目標軟體,安裝更安全
  • 登錄頁面優化:盤點6款提升轉化的登錄頁面生成器
    這裡有6個工具,可以幫你生成一個成功的亞馬遜登錄頁面,並在你想看到的地方獲得流量數據。 為什麼要使用亞馬遜的登錄頁面生成器而不是自己創建呢? 有些事情你可以自己做,有些事情是值得花錢請專業人士來處理的。除非你有充分的時間和技能,這就是為什麼使用亞馬遜登錄頁面生成器可以真正幫助到你的原因。 ●工作量變少了。
  • 詞雲圖生成器
    詞雲圖生成器 生活工具 大小: 15.48MB
  • Andes SAG應用實例
    我們收到的反饋也證明,越來越多的工程師開始採用Andes SAG替代linker,之前我們有一篇技術文章對SAG的語法格式做了介紹並說明如何使用,本文將展示四個實際工程開發的例子,以幫助廣大開發者更好的熟悉和理解Andes SAG,同時可以作為開發時的參考。
  • es6 的 class 與 es5 的語法對比
    ES6 提供了更接近傳統語言的寫法,引入了 Class(類)這個概念,作為對象的模板。通過 class 關鍵字,可以定義類。基本上,ES6 的class可以看作只是一個語法糖,它的絕大部分功能,ES5 都可以做到,新的class寫法只是讓對象原型的寫法更加清晰、更像面向對象編程的語法而已class
  • Python生成器next方法和send方法區別詳解
    時間:2020-05-30Python字典生成式、集合生成式、生成器用法實例分析本文實例講述了Python字典生成式.集合生成式.生成器用法.分享給大家供大家參考,具體如下: 字典生成式: 跟列表生成式一樣,字典生成式用來快速生成字典,不同的是,字典需要兩個值 #d = {key: value for (key, value) in iterable} d1 = {'x'
  • LOGO在線製作生成器到底哪個好?
    在網際網路時代,製作淘寶店鋪LOGO、拼多多LOGO以及各種網站LOGO,很多人都喜歡用logo在線製作生成器生成。那麼Logo在線製作生成器哪個好呢?今天我們就給大家分享多個LOGO在線製作生成器,分析其優劣勢,助您選擇。
  • 營銷號生成器的工作原理是什麼 有哪些好玩的營銷號生成器
    在B站上,你甚至可以看到上百個關於「營銷號生成器」的相關內容。,文章生成器能生成什麼樣的文章?」甚至還有人用Python寫了個營銷號視頻自動生成器~目前,這類「傻瓜文案生成器」工具非常多,網友比較常用的幾種有:傻瓜文案生成器、愛情小說生成器、CP短打生成器、今日吃什麼、萬能表情包生成器、沙雕動圖生成器等。
  • 二維碼文字生成器
    二維碼文字生成器 生活工具 大小: 5.11M
  • 6款好用的微信對話生成器
    今天,小編就給大家介紹一下使用微信對話生成器編輯微信對話的方法,給大家推薦了6款好用的微信對話生成器希望大家喜歡!玩截圖玩截圖6款微信對話生成器推薦:特別說明:該軟體僅供娛樂,請勿用於非法途徑。一、玩截圖微信對話生成器網頁版支持外觀設置和對話設置,具體內容包括:(一)外觀設置1、信號強度:1格2格3格4格5格2、運營商:中國移動中國聯通中國電信3、網絡信號:無WifiGE3G4G4、手機時間:5、鎖定旋轉:顯示不顯示6、電量:+7、電池狀態:正常充電中
  • Javascript 生成器
    在 javascript 中,如果想要使用生成器,則需要: 定義特殊的生成器函數 調用該函數創建一個生成器對象 在循環中使用該生成器對象,或直接調用其 方法 我們以下面這個簡單的程序做為起點,並執行以下每個步驟:
  • JeecgBoot 2.1.1 代碼生成器 AI 版本發布,基於 SpringBoot 的快速...
    項目介紹JeecgBoot 是一款基於 SpringBoot+代碼生成器的快速開發平臺!採用前後端分離架構:SpringBoot,Ant-Design-Vue,Mybatis-plus,Shiro,JWT。強大的代碼生成器讓前端和後臺代碼一鍵生成,不需要寫任何代碼,保持 jeecg 一貫的強大,絕對是全棧開發福音!!
  • 類似傻瓜文案生成器的有哪些?微博超火生成器網頁地址大全[視頻...
    傻瓜文案生成器的玩法很多,微博上也出現了很多熱門的生成器玩法,這次小編會分享一些超級火爆的生成器,下面就是這次小編要給大家分享的內容,想要知道傻瓜文案生成器的玩法和其他生成器的入口,都可以參考下面的攻略哦!
  • WIFI二維碼生成器
    WIFI二維碼生成器 系統安全 大小: 1.62MB
  • 提高 Python:解釋 yield 和 生成器
    (點擊上方藍字,快速關注我們)編譯:oschina,英文:Jeff Knuppwww.oschina.net/translate
  • Python中迭代器和生成器的區別?
    廢話不多說,開始今天的題目:問:說說Python中迭代器和生成器的區別?答:Python中生成器能做到迭代器能做的所有事,而且因為自動創建了__iter__()和next()方法,生成器顯得特別簡潔,而且生成器也是高效的,使用生成器表達式取代列表解析,同時節省內存。