帶你重學ES6 | Generator

Generator 是 ES6 提出的一種異步編程的解決辦法,它與傳統的函數徹底不一樣,本章從基礎概念和基本用法進行講解和解析。在此以前也是對 Generator 函數雲裏霧裏,因此經過這次學習,但願能對 Generator 有更深的理解和認識。前端

1.概念

Generator 中文的意思是'生成器',阮一峯:ECMAScript 6 入門中對 Generator 解釋是: Generator 函數有多種理解角度。語法上,首先能夠把它理解成,Generator 函數是一個狀態機,封裝了多個內部狀態。git

執行 Generator 函數會返回一個遍歷器對象,也就是說,Generator 函數除了狀態機,仍是一個遍歷器對象生成函數。返回的遍歷器對象,能夠依次遍歷 Generator 函數內部的每個狀態。 Generator 有兩個特色: 第一個是在定義時,要在 function 關鍵字和函數名中加一個星號,第二個就是在函數體中運用 yeild 表達式表示不一樣的狀態。es6

function* foo() {
 yield "a";  yield "b"; } let f = foo(); console.log(f); 複製代碼

和普通函數不一樣的是,當運行這個函數時,返回不是這個函數的結果,而是一個遍歷器對象,或者能夠說是一個含有內部狀態指針的對象。 若是想輸出值得話,要用 next 方法來進行輸出。next 方法就是向下移動指針,即每次調用 next 方法,就是從函數頭部或者從上一次 yield 表達式移動到下一次 yield 表達式(或者 return 爲止)。github

function* foo() {
 yield "a";  yield "b"; } let f = foo(); f.next(); // {value: "a", done: false} f.next(); // {value: "b", done: false} f.next(); // {value: undefined, done: true} 複製代碼

上述能夠看出,調用 next 時輸出的是一個對象,即 value 值表明 yield 後面的結果,done 表明遍歷尚未結束,當遍歷結束時,value 值都爲 undefined,done 都爲 true。編程

2.yield 表達式

yield 有個懶惰的特性,即 yield 後面的表達式,若是不調用 next 方法,是不會執行的。markdown

function* foo() {
 yield 1 + 1; } foo().next(); // 2 複製代碼

只有當 next 指針移動到該 yield 的時候,纔會執行後面的表達式。 yield 和 return 是有不一樣之處的,在 Generator 函數中,能夠定義多個 yield 表達式,可是 return 只能定義一個,而且在 yield 中遍歷尚未完成,但在遇到 return 時,遍歷就終止了。異步

function* foo() {
 yield "a";  yield "b";  return "c"; } let f = foo(); f.next(); // {value: "a", done: false} f.next(); // {value: "b", done: false} f.next(); // {value: "c", done: true} f.next(); // {value: undefined, done: true} 複製代碼

當遇到 return 時,遍歷結束 done 爲 true,value 值爲 return 後的結果,在此以後的 next 的結果都爲{value: undefined, done: true}。 若是在 Generator 函數中沒有 yield 表達式:ide

function* foo() {
 console.log("a"); } let f = foo(); f.next(); 複製代碼

foo()返回的依舊是一個含有內部狀態指針的對象,只有當 next 方法執行時該函數纔會執行。 當 yield 和其餘表達式融合時,若是 yield 表達式在左邊,要將 yield 表達式用圓括號包裹,不然就會報語法錯誤。異步編程

function* foo() {
 console.log('a'+ yield 'b'); // Uncaught SyntaxError: Unexpected identifier } function* foo() {  console.log('a'+ (yield 'b')); // {value: "b", done: false} } let f = foo(); f.next(); 複製代碼

當用做函數參數或放在賦值表達式的右邊,能夠不加括號。函數

function* demo() {
 foo(yield "a", yield "b"); // OK  let input = yield; // OK } 複製代碼

3.next 方法

yield 是沒有返回值的,它的返回值是 undefined,咱們能夠經過 next 方法將參數傳遞給 yield,此參數將會爲 yield 的返回值。

function* f() {
 for (var i = 0; true; i++) {  var reset = yield i;  if (reset) {  i = -1;  }  } } var g = f(); g.next(); // { value: 0, done: false } g.next(); // { value: 1, done: false } g.next(true); // { value: 0, done: false } 複製代碼

上面代碼先定義了一個能夠無限運行的 Generator 函數 f,若是 next 方法沒有參數,每次運行到 yield 表達式,變量 reset 的值老是 undefined。當 next 方法帶一個參數 true 時,變量 reset 就被重置爲這個參數(即 true),所以 i 會等於-1,下一輪循環就會從-1 開始遞增。 不用 next 方法是否能夠輸出值呢?答案是能夠的,能夠用 for...of...方法遍歷 Generator 函數。

function* foo() {
 yield 1;  yield 2;  yield 3;  yield 4;  yield 5;  return 6; }  for (let v of foo()) {  console.log(v); // 1 2 3 4 5 } 複製代碼

值得注意的是,for...of...在 done 爲 ture 的時候就會終止執行,因此 return 後的 6 沒有輸出。

4.yield* 表達式

yield* 表達式是爲了解決在一個 Generator 函數中調用另外一個 Generator 函數所提供的方法。

function* foo() {
 yield 1;  yield 2;  yield 3;  yield* foo1(); } 等同於; function* foo() {  yield 1;  yield 2;  yield 3;  for (let v of foo1()) {  yield v;  } } 複製代碼

當 yield*後邊的 Generator 函數中沒有 return 時,做用就是 for...of...遍歷 Generator 函數,若是 Generator 函數中有 return 時,則獲取的是 return 值。

function* foo() {
 yield 1;  yield 2;  yield 3;  var value = yield* foo1();  console.log(value); } function* foo1() {  yield 4;  return 5; } let f = foo(); f.next(); // {value: 1, done: false} f.next(); // {value: 2, done: false} f.next(); // {value: 3, done: false} f.next(); // 5 {value: undefined, done: true} 複製代碼

其實 yield*後面只要是帶有 Iterator 接口的都會被遍歷。

function* foo() {
 yield* [1, 2, 3, 4]; } let f = foo(); f.next(); // {value: 1, done: false} 複製代碼

再舉個例子:

function* foo() {
 yield "a";  yield "b";  return "END"; } function* bar(func) {  let result = yield* func();  console.log(result); } [...bar(foo)]; // END // ["a", "b"] 複製代碼

上述中 foo 是擁有 return 表達式的函數,因此 return 後的結果會‘賦值’給 yield*表達式的返回值,因此 result 是'END',而且拓展運算符默認調用 Iterator 接口,因此會先打印出 result,而後再執行 yield。

參考:

阮一峯:ECMAScript 6 入門

後語

相關文章:

以爲還能夠的,麻煩走的時候能給點個贊,你們一塊兒學習和探討!

還能夠關注個人博客但願能給個人github上點個Start,小夥伴們必定會發現一個問題,個人全部用戶名幾乎都與番茄有關,由於我真的很喜歡吃番茄❤️!!!

想跟車不迷路的小夥還但願能夠關注公衆號 前端老番茄 或者掃一掃下面的二維碼👇👇👇。

我是一個編程界的小學生,您的鼓勵是我不斷前進的動力,😄但願能一塊兒加油前進。

本文使用 mdnice 排版

相關文章
相關標籤/搜索