91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

ES6中Generator函數的使用方法

發布時間:2020-10-10 17:46:45 來源:億速云 閱讀:134 作者:小新 欄目:web開發

這篇文章主要介紹了ES6中Generator函數的使用方法,具有一定借鑒價值,需要的朋友可以參考下。希望大家閱讀完這篇文章后大有收獲。下面讓小編帶著大家一起了解一下。

一、什么是Generator函數

Generator函數是ES6標準中提出的一種異步編程的解決方案。這種函數與普通函數最大的區別在于它可以暫停執行,又可以從暫停的位置恢復繼續執行。

從語法上看,Generator函數就是一個狀態機,封裝了許多內部狀態。

從實質上看,Generator函數就是一個遍歷器對象生成器。(關于遍歷器對象,可以參考阮一峰老師的這篇文章)Generator函數返回一個遍歷器對象,遍歷這個對象,就可以依次得到函數內部的每一個狀態。

二、基本語法

1、定義Generator函數

定義一個Generator函數和定義一個普通函數的區別在于:

function關鍵字和函數名之間有一個 *(星號)。
函數內部使用yield來定義每一個函數內部的狀態。
如果函數內部有return語句,那么他就是函數內部的最后一個狀態。

來看一個簡單的例子:

// 定義
function* sayHello() {
  yield 'hello';
  yield 'world';
  return 'ending';
}
// 調用
// 注意,hw獲取到的值是一個遍歷器對象
let g = sayHello();

上面的例子,定義了一個名為sayHello的Generator函數,它內部有兩個yield表達式和一個return表達式。所以,該函數內部有三個狀態:helloworldreturn語句(結束執行)。最后,調用這個函數,得到一個遍歷器對象并賦值給變量g

Generator函數的調用方法與普通函數完全一樣,函數名()。不同的是:

  • 函數調用后,內部代碼(從第一行開始)都不會立即執行。
  • 函數調用后會有一個返回值,這個值是一個指向內部狀態的指針對象,實質就是一個包含函數內部狀態的遍歷器對象。

Generator函數調用后不會立即執行,那么,我們如何讓它開始執行內部的代碼呢?又如何獲取它內部的每一個狀態呢?此時,我們必須調用返回的生成器對象的.next()方法,才能開始代碼的執行,并且使得指針移向下一個狀態。

以上面的例子為例:

g.next();
// { value: 'hello', done: false }
g.next();
// { value: 'world', done: false }
g.next();
// { value: 'ending', done: true }
g.next();
// { value: undefined, done: true }

上面的代碼中,一共調用了四次g這個遍歷器對象的.next()方法。第一次調用,sayHello這個Generator函數開始執行,直到遇到第一個yield表達式就會暫停執行。.next()方法會返回一個對象,它的value屬性就是當前yield表達式的值hellodone屬性的值false,表示遍歷還沒有結束。

第二次再調用.next(),就會執行到第二個yield表達式處,并暫停執行,返回對應的對象。

第三次調用.next(),函數執行到最后的return語句,此時標志著遍歷器對象g遍歷結束,所以返回的對象中value屬性值就是return后面所跟的值endingdone屬性值為true,表示遍歷已經結束。

第四次以及后面在調用.next()方法,返回的都會是{value: undefined, done: true }

2、yield表達式

由Generator函數返回的遍歷器對象,只有調用.next()方法才會遍歷到下一個內部狀態,所以這其實是提供了一種可以暫停執行的函數,yield表達式就是暫停標志。

遍歷器對象的.next()方法的運行邏輯如下。

  1. 遇到yield表達式,就暫停執行后面的操作,并將緊跟在yield后面的那個表達式的值,作為返回的對象的value屬性值。
  2. 下一次調用.next()方法時,再繼續往下執行,直到遇到下一個yield表達式。
  3. 如果沒有再遇到新的yield表達式,就一直運行到函數結束,直到return語句為止,并將return語句后面的表達式的值,作為返回的對象的value屬性值。
  4. 如果該函數沒有return語句,則返回的對象的value屬性值為undefined

值得注意的是:

  1. yield關鍵字只能出現在Generator函數中,出現在別的函數中會報錯。
  // 出現在普通函數中,報錯
  (function () {
    yield 'hello';
  })()

  // forEach不是Generator函數,報錯
  [1, 2, 3, 4, 5].forEach(val => {
    yield val
  });
  1. yield關鍵字后面跟的表達式,是惰性求值的。 只有當調用.next()方法、內部狀態暫停到當前yield時,才會計算其后面跟的表達式的值。這等于為JavaScript提供了手動的“惰性求值”的語法功能。
function* step() {
  yield 'step1';

  // 下面的yield后面的表達式不會立即求值,
  // 只有暫停到這一行時,才會計算表達式的值。
  yield 'step' + 2;

  yield 'setp3';
  return 'end';
}
  1. yield表達式本身是沒有返回值的,或者說它的返回值為undefined。使用.next()傳參可以為其設置返回值。(后面會講到)
function* gen() {
  for (let i = 0; i < 5; i++) {
    let res = yield;  // yield表達式本身沒有返回值
    console.log(res); // undefined
  }
}
let g = gen();
g.next();   // {value: 0, done: false}
g.next();   // {value: 1, done: false}
g.next();   // {value: 2, done: false}

yield與return的異同:

相同點:

  • 兩者都能返回跟在其后面的表達式的值。

不同點:

  • yield表達式只是暫停函數向后執行,return是直接結束函數執行。
  • yield表達式可以出現多次,后面還可以有代碼。return只能出現一次,后面的代碼不會執行,在一些情況下還會報錯。
  • 正常函數只能返回一個值,因為只能執行一次return。Generator函數可以返回一系列的值,因為可以有任意多個yield。
3、.next()方法傳參

前面我們說到過,yield表達式自身沒有返回值,或者說返回值永遠是undefined。但是,我們可以通過給.next()方法傳入一個參數,來設置上一個(是上一個)yield表達式返回值。

來看一個例子:

function* conoleNum() {
  console.log('Started');
  console.log(`data: ${yield}`);
  console.log(`data: ${yield}`);
  return 'Ending';
}
let g = conoleNum();

g.next();      // 控制臺輸出:'Started'

g.next('a');   // 控制臺輸出:'data: a'
// 不傳入參數'a',就會輸出'data: undefined'

g.next('b');   // 控制臺輸出:'data: b'
// 不傳入參數'a',就會輸出'data: undefined'

上面的例子,需要強調一個不易理解的地方。

第一次調用.next(),此時函數暫停在代碼第三行的yield表達式處。記得嗎?yield會暫停函數執行,此時打印它的console.log(),也就是代碼第三行的console,由于暫停并沒有被執行,所以不會打印出結果,只輸出了代碼第二行的'Started'。

當第二次調用.next()方法時,傳入參數'a',函數暫停在代碼第四行的yield語句處。此時參數'a'會被當做上一個yield表達式的返回值,也就是代碼第三行的yiled表達式的返回值,所以此時控制臺輸出'data: a'。而代碼第四行的console.log()由于暫停,沒有被輸出。

第三次調用,同理。所以輸出'data: b'

4、Generator.prototype.throw()

Generator函數返回的遍歷器對象,都有一個.throw()方法,可以在函數體外拋出錯誤,然后在Generator函數體內捕獲

function* gen() {
  try {
    yield;
  } catch (e) {
    console.log('內部捕獲', e);
  }
};

var g = gen();
// 下面執行一次.next()
// 是為了讓gen函數體執行進入try語句中的yield處
// 這樣拋出錯誤,gen函數內部的catch語句才能捕獲錯誤
g.next();

try {
  g.throw('a');
  g.throw('b');
} catch (e) {
  console.log('外部捕獲', e);
}

上面例子中,遍歷器對象ggen函數體外連續拋出兩個錯誤。第一個錯誤被gen函數體內的catch語句捕獲。g第二次拋出錯誤,由于gen函數內部的catch語句已經執行過了,不會再捕捉到這個錯誤了,所以這個錯誤就會被拋出gen函數體,被函數體外的catch語句捕獲。

值得注意的是:

  • 如果Generator函數內部沒有部署try...catch代碼塊,那么遍歷器對象的throw方法拋出的錯誤,將被外部try...catch代碼塊捕獲。
  • 如果Generator函數內部和外部都沒有部署try...catch代碼塊,那么程序將報錯,直接中斷執行。

遍歷器對象的throw方法被捕獲以后,會附帶執行一次.next()方法,代碼執行會暫停到下一條yield表達式處。看下面這個例子:

function* gen(){
  try {
    yield console.log('a');
  } catch (e) {
    console.log(e);   // 'Error'
  }
  yield console.log('b');
  yield console.log('c');
}
var g = gen();

g.next();   // 控制臺輸出:'a'

g.throw('Error');  // 控制臺輸出:'b'
// throw的錯誤被內部catch語句捕獲,
// 會自動在執行一次g.next()

g.next();   // 控制臺輸出:'c'
5、Generator.prototype.return()

Generator函數返回的遍歷器對象,還有一個.return()方法,可以返回給定的值,并且直接結束對遍歷器對象的遍歷。

function* gen() {
  yield 1;
  yield 2;
  yield 3;
}
var g = gen();

g.next();        // { value: 1, done: false }

// 提前結束對g的遍歷。盡管yield還沒有執行完
// 此時done屬性值為true,說明遍歷結束
g.return('foo'); // { value: "foo", done: true }

g.next();        // { value: undefined, done: true }

如果.return()方法調用時,不提供參數,則返回值的value屬性為undefined

6、yield* 表達式

yield* 用來在一個Generator函數里面執行另一個Generator函數。

如果在一個Generator函數內部,直接調用另一個Generator函數,默認情況下是沒有效果的。

function* gen1() {
  yield 'a';
  yield 'b';
}
function* gen2() {
  yield 'x';
  // 直接調用gen1()
  gen1();
  yield 'y';
}
// 遍歷器對象可以使用for...of遍歷所有狀態
for (let v of gen2()){
  只輸出了gen1的狀態
  console.log(v);   // 'x' 'y'
}

上面的例子中,gen1gen2都是Generator函數,在gen2里面直接調用gen1,是不會有效果的。

這個就需要用到 yield* 表達式。

function* gen1() {
  yield 'a';
  yield 'b';
}
function* gen2() {
  yield 'x';
  // 用 yield* 調用gen1()
  yield* gen1();
  yield 'y';
}

for (let v of gen2()){
  輸出了gen1、gen2的狀態
  console.log(v);   // 'x' 'a' 'b' 'y'
}

感謝你能夠認真閱讀完這篇文章,希望小編分享ES6中Generator函數的使用方法內容對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,遇到問題就找億速云,詳細的解決方法等著你來學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

平江县| 高台县| 邓州市| 宁蒗| 石河子市| 中山市| 绩溪县| 皮山县| 汉源县| 泰来县| 北流市| 阿拉尔市| 马尔康县| 宝清县| 龙游县| 丽江市| 镇原县| 太仆寺旗| 富顺县| 巴马| 运城市| 东阿县| 仁布县| 九龙城区| 都安| 旺苍县| 钦州市| 华安县| 鹤岗市| 东乡| 天峨县| 正蓝旗| 祁门县| 崇文区| 乌恰县| 永定县| 格尔木市| 宜阳县| 故城县| 菏泽市| 通江县|