- 原文地址:A Simple Guide to Understanding Javascript (ES6) Generators
- 原文做者:Rajesh Babu
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:ssshooter
- 校對者:Zheng7426 hopsken
若是你在過去兩到五年中一直在研究 JavaScript,那麼確定看過關於 Generator 和 Iterator 的文章。雖然 Generator 和 Iterator 本質上是相關的,但 Generator 彷佛比 Iterator 更使人難以理解。javascript
Iterator 由 Iterable 對象(如 map,數組和字符串等)實現,咱們可以使用 next() 迭代它們。Iterator 在 Generator,Observable 和 Spread 運算符中普遍使用。前端
若是你剛接觸 Iterator,建議先閱讀 Guide to Iterators。java
可使用內建的 Symbol.iterator 驗證對象是否符合可迭代要求:android
new Map([[1, 2]])[Symbol.iterator]() // MapIterator {1 => 2}
「hi」[Symbol.iterator]() // StringIterator {}
[‘1’][Symbol.iterator]() // Array Iterator {}
new Set([1, 2])[Symbol.iterator]() // SetIterator {1, 2}
複製代碼
第一次亮相於 ES6 的 Generator 在後續 JavaScript 版本的發佈中並無變化,因此 Generator 有可能在未來會繼續保持如今的特性及用法,咱們是繞不開它的。雖然 ES7 和 ES8 有一些小更新,可是改變幅度沒法與 ES5 到 ES6 相提並論,能夠說 ES6 使得 JavaScript 踏出了新的一步。ios
讀完本文,我相信你必定能充分理解 Generator 的原理。若是你是專業人士,歡迎在回覆中添加評論,一塊兒改進這篇文章。爲幫助你們理解代碼,代碼中已包含必定註釋。git
衆所周知,JavaScript 的函數都會一直運行到 return 或函數結束。但對於 Generator 函數,會一直運行到 遇到 yield 或 return 或函數結束。與通常函數不一樣,Generator 函數一旦被調用,就會返回一個 Generator 對象。這個對象擁有 Generator Iterable,可使用 next() 方法或 for…of 循環迭代它。es6
Generator 每次調用 next(),函數會一直運行到下一個 yield,而後暫停執行。github
語法上他們的標誌是一個星號 *,function* X 和 function *X 的效果相同。後端
Generator 函數返回 Generator 對象。要把 Generator 對象賦值到一個變量,才能方便地使用它的 next() 方法。 若是沒有把 Generator 分配給變量,對它調用 next() 老是隻會運行到第一個 yield 表達式。api
Generator 函數中一般含有 yield 表達式。Generator 函數內的每一個 yield 都是下一個執行循環開始以前的中止點。每一個執行週期都經過 Generator 的 next() 方法觸發。
每次調用 next(),yield 表達式都會返回包含如下參數的對象。
{ value: 10, done: false } // 假設 yield 的值是 10
(若是你沒法理解上面說的是什麼,那下面的例子可能會讓你理解得更清晰……)
Generator 函數基礎
**注意:**在上面的例子中,直接訪問 Generator 函數老是執行到第一個 yield。所以,你須要將 Generator 分配給變量才能正確迭代它。
在深刻理解以前,讓咱們快速瀏覽一下 Generator 函數的生命週期示意圖:
Generator 函數的生命週期
每次運行到 yield,Generator 函數都會返回一個對象,該對象包含 yield 產生的值和當前 Generator 函數的狀態。相似地,運行到 return,能夠獲得 return 的值,而且 done 的狀態爲 true。當 done 的狀態爲 true 時,意味着 Generator 函數已經運行完畢,後面的 yield 通通無效。
return 後的一切代碼都會被忽略,包括 yield 表達式。
繼續閱讀深刻理解上圖。
在的示例中,咱們建立了一個帶有 yield 的最基本的 Generator,並得到了預期的輸出。在下面代碼中,咱們將整個 yield 表達式賦值到一個變量。
把 yield 賦值到一個變量
把整個 yield 表達式傳到變量的結果是什麼?Undefined …
爲何會是 undefined?從第二個 next() 開始,前一個 yield 會被替換爲 next 函數的參數。由於例子中的 next 沒有傳入任何值,因此程序斷定「前一個 yield 表達式」爲 undefined。
這是重點中的重點,下面的章節咱們將詳細介紹對 next() 傳參的用法。
參考上面的示意圖,咱們聊聊關於傳參到 next 函數的事情。這是整個 Generator 使用中最棘手的部分之一。
思考如下代碼,其中 yield 被賦給變量,但此次咱們向 next() 傳參。
看看控制檯的輸出,先思考一下,後面會有解釋。
將參數傳遞給 next()
這可能對初次接觸 Generator 的讀者有點超綱,可是給本身 5 分鐘,多讀幾遍,就能清楚明白。
Yield 在 Generator 中還有大把的用法,咱們接着看看下面的代碼,這是 yield 的其中一個妙用,附帶解釋。
Yield 做爲其餘函數的參數
'I am usless'
,這一步爲函數調用準備了參數。除了返回普通的值,yield 還能夠調用函數並返回他的值。看看下面的例子更好理解:
對函數調用使用 yield
上述代碼返回了函數返回的對象做爲 yield 的 value,而後把 const user 賦值爲 undefined,結束運行。
對 promise 使用 yield 與對函數調用使用 yield 類似,它會返回一個 promise,咱們以此進一步斷定操做成功或失敗。看看如下代碼,瞭解它的使用方法:
對 Promise 使用 yield
apiCall 將 promise 做爲 yield value 返回,在 2 秒後 resolved 並打印出咱們須要的值。
Yield*
Yield 表達式的介紹就告一段落了,接着咱們瞭解一下另外一個表達式 yield*
。Yield*
在 Generator 函數中使用時,會把迭代委託到下一個 Generator 函數。簡單來講,會先同步完成 Yield*
表達式中的 Generator 函數,再繼續運行外層函數。
讓咱們看看下面的代碼和解釋,以便更好地理解。此代碼來自 MDN Web 文檔。
Yield*
基礎
yield*
表達式,這意味着咱們要先完成 yield*
表達式指定的 Generator 函數,再繼續運行當前 Generator 函數。function* g2() {
yield 1;
yield 2;
yield 3;
yield 4;
yield 5;
}
複製代碼
Generator 會按這個順序運行結束。不過對於
yield*
和 return 的同時使用,咱們須要特別注意,下一節將會提到。
Yield*
與 Return帶 return 的 yield*
與通常 yield*
有點不一樣。當 yield*
與 return 語句一塊兒使用時,yield*
被賦 return 的值,也就是整個 yield*
function() 與其關聯 Generator 函數的返回值相等。
讓咱們看看下面的代碼和解釋,以便更好理解。
Yield*
與 Return
Yield*
yield*
還有一個值得一提的用法,它能夠遍歷 iterable 對象,如 Array,String 和 Map。
一塊兒看看實際運行結果。
對內建 Iterable 對象使用 Yield*
在代碼中,yield*
遍歷傳入的每個 iterable 對象,我以爲這段代碼自己是不言自明的。
最重要的是,每一個 iterator/Generator 均可以使用 for…of 遍歷。與顯式調用的 next() 相似,for…of 循環依據 yield 關鍵字 進入下一次迭代。這裏是重點:它只會迭代到最後一個 yield,不會像 next() 那樣處理 return 語句。
下面的代碼能夠驗證以上描述。
Yield 與 for…of
最後 return 的值不會被打印,由於 for…of 循環只迭代到最後一個 yield。所以,做爲最佳實踐,儘可能避免在 Generator 函數中使用 return 語句,緣由在於當使用 for…of 語句進行迭代時,return 會影響函數的可重用性。
我但願這涵蓋了 Generator 函數的基本用法,但願這篇文章能讓你更好地理解 Generator 在 JavaScript 中的工做方式。若是你喜歡本文,請點個贊吧 :)。
請關注個人 GitHub 帳號獲取更多 JavaScript 和全棧項目:
若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。