第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

全部開發(fā)者教程

ES6-10 入門教程

ES6+ Generator 基礎(chǔ)

1. 前言

前面我們花了三節(jié)深入地學(xué)習(xí)了 ES6 的異步解決方案 Promise,本節(jié)學(xué)習(xí)的生成器也是為了解決異步而生的,但是它的出發(fā)思路和 Promise 截然不同。

上節(jié)我們學(xué)習(xí)了 ES6 中 迭代 的相關(guān)內(nèi)容,并實(shí)現(xiàn)了一個迭代器。我們知道實(shí)現(xiàn)一個迭代器,我們需要手動添加對象的 Symbol.iterator 屬性,并需要實(shí)現(xiàn) next 方法。那么有沒有什么可以幫助我們自動實(shí)現(xiàn)迭代器呢?ES6 給出了生成器的方法來滿足我們的需求。我們不需要在對象上添加 Symbol.iterator 屬性,使用生成器函數(shù)就可以實(shí)現(xiàn)迭代器的功能。本節(jié)我們將學(xué)習(xí)生成器的相關(guān)概念和基礎(chǔ)用法。

生成器是一個靈活的結(jié)構(gòu),能使得一個函數(shù)塊內(nèi)部暫停和恢復(fù)代碼執(zhí)行的能力。在實(shí)際應(yīng)用中,使用生成器可以自定義迭代器和協(xié)程。

2. 生成器對象和生成器函數(shù)

有些概念是我們必須要理解的,前面在學(xué)習(xí)迭代器的時候,我們學(xué)習(xí)了迭代協(xié)議和迭代器協(xié)議,實(shí)現(xiàn)一個迭代器需要滿足這兩個協(xié)議才算是一個真正的迭代器。而本節(jié)的生成器和生成器函數(shù)也是如此,我們也需要知道生成器對象和生成器函數(shù)概念和它們直接的關(guān)系。

Generator 就是我們說的生成器,它包含兩個概念 生成器對象和生成器函數(shù)。首先,要理解的是生成器對象和迭代器的關(guān)系,生成器對象是遵守迭代協(xié)議和迭代器協(xié)議實(shí)現(xiàn)的 Iterable 接口,可以理解生成器對象其實(shí)也是一個迭代器;然后,我們需要理解什么是生成器函數(shù),生成器函數(shù)是由 function * 來定義的,并且返回結(jié)果是一個 Generator 對象。

生成器是一個特殊的函數(shù),在調(diào)用后會返回一個生成器對象,這個生成器對象是遵守可迭代協(xié)議和迭代器協(xié)議實(shí)現(xiàn)的 Iterable 接口。生成器可以使用 yield 關(guān)鍵字來暫停執(zhí)行的生成器函數(shù):

function* generator() {
  yield 'a';
  yield 'b';
}

var gen = generator();	// Object [Generator] {}

2.1 Generator.prototype.next()

生成器的 next () 方法和迭代器返回的結(jié)果是一樣的,返回了一個包含屬性 donevalue 的對象,該方法也可以通過接受一個參數(shù)用以向生成器傳值。

使用 yield 返回的值會被迭代器的 next () 方法捕獲:

var gen = generator();

gen.next()	// {value: 'a', done: false}
gen.next()	// {value: 'b', done: false}
gen.next()	// {value: undefined, done: true}

從上面代碼的執(zhí)行結(jié)果可以看出,生成器函數(shù)在執(zhí)行后會返回一個生成器對象,這個生成器對象滿足迭代協(xié)議和迭代器協(xié)議,所以我們可以去手動調(diào)用它的 next () 方法去獲取每一步的返回值。從這里可以看出,生成器其實(shí)就是迭代器的一個應(yīng)用,并且這個應(yīng)用會在異步中大放異彩。

2.2 Generator.prototype.return()

return() 方法返回給定的值并結(jié)束生成器。

var gen = generator();

gen.next();        // { value: 'a', done: false }
gen.return("imooc"); // { value: "imooc", done: true }
gen.next();        // { value: undefined, done: true }

另外,如果對已經(jīng)完成狀態(tài)的生成器調(diào)用 return(value) 則生成器會一直保持在完成狀態(tài),如果出入?yún)?shù),value 會設(shè)置成傳入的參數(shù),done 的值不變:

var gen = generator();

gen.next(); // { value: 1, done: false }
gen.next(); // { value: 2, done: false }
gen.next(); // { value: undefined, done: true }
gen.return(); // { value: undefined, done: true }
gen.return(1); // { value: 1, done: true }

2.2 Generator.prototype.throw()

throw() 方法用來向生成器拋出異常,并恢復(fù)生成器的執(zhí)行,返回帶有 donevalue 兩個屬性的對象。

function* generator() {
  while(true) {
    try {
       yield 'imooc'
    } catch(e) {
      console.log("Error caught!");
    }
  }
}
var gen = generator();
gen.next(); // { value: "imooc", done: false }
gen.throw(new Error("error")); // "Error caught!"

3. Generator 案例

3.1 類數(shù)組轉(zhuǎn)化

將一個類數(shù)組轉(zhuǎn)化為一個真正的數(shù)組方式有很多,ES6 提供了 Array.from() 可以將類數(shù)組轉(zhuǎn)化為數(shù)組 。另外在一些函數(shù)中可以使用 [...argument] 的方式轉(zhuǎn)化類數(shù)組。

function fn() {
  const arg = [...arguments];
  console.log(arg);
}
fn(1, 2, 3);	// [1, 2, 3]

當(dāng)然我們知道類數(shù)組的定義,所以我們自己定義一個類數(shù)組,看能不能使用展開運(yùn)算符將類數(shù)組轉(zhuǎn)化為數(shù)組:

const likeArr = {
  0: 1,
  1: 2,
  length: 2,
}
console.log([...likeArr]);	// Uncaught TypeError: likeArr is not iterable

上面代碼中我們定義了一個類數(shù)組,但是使用展開運(yùn)算符報錯了,提示我們 likeArr 不是一個迭代器。因?yàn)樵诤瘮?shù)中類數(shù)組是內(nèi)部幫我們實(shí)現(xiàn)了迭代器的功能,而我們自己定義的類數(shù)組是不具有迭代器功能的,那我們來自己實(shí)現(xiàn)一個:

likeArr[Symbol.iterator] = function() {
  let index = 0;
  return {
    next: () => {
      return { value: this[index], done: index++ === this.length}
    }
  }
}
console.log([...likeArr]);	// [1, 2]

上面的代碼我們在 likeArr 對象上定義了 Symbol.iterator 它具有迭代功能。上面代碼中我們需要手動地去實(shí)現(xiàn) next () 方法,這比較麻煩,那能不能簡化一下呢?我們的生成器函數(shù)就出場了:

likeArr[Symbol.iterator] = function* () {
  let index = 0;
  while (index !== this.length) {
    yield this[index++];
  }
}
console.log([...likeArr]);	// [1, 2]

上面的代碼使用了生成器函數(shù),并且沒有去手動實(shí)現(xiàn) next () 方法,從這里我們也能很清楚地知道迭代器和生成器的關(guān)系。而且使用生成器函數(shù)更加簡單方便。

3.2 單步獲取質(zhì)數(shù)

還有一個案例是面試中經(jīng)常會考到的:

題目:實(shí)現(xiàn)一個函數(shù),每次調(diào)用返回下一個質(zhì)數(shù),要求不使用全局變量,且函數(shù)本身不接受任何參數(shù)

從題目的要求可以知道,這個函數(shù)每次調(diào)用都會返回一個質(zhì)數(shù),也就是說每次調(diào)用后都會返回一個函數(shù)。

首先我們定義一個判斷一個數(shù)是否為質(zhì)數(shù)的方法:

function isPrime(num) {
  for (let i = 2; i <= Math.sqrt(num); i++) {
    if (num % i === 0) {
      return false
    }
  }
  return true
}

傳統(tǒng)的方式是使用閉包方法來解決:

function primeHandler() {
  let prime = 1
  return () => {
    while (true) {
      prime++
      if (isPrime(prime)) {
        return prime
      }
    }
  }
}

const getPrime = primeHandler()
console.log(getPrime());	// 2
console.log(getPrime());	// 3
console.log(getPrime());	// 5

既然是單步執(zhí)行的,那么我們就可以使用迭代器方式實(shí)現(xiàn):

var prime = {}
prime[Symbol.iterator] = function() {
  let prime = 1;
  return {
    next() {
      while(true) {
        prime++
        if (isPrime(prime)) {
          return prime;
        }
      }
    }
  }
}
var getPrime = prime[Symbol.iterator]().next;
console.log(getPrime());	// 2
console.log(getPrime());	// 3

上一個實(shí)例我們知道實(shí)現(xiàn)迭代器的方式是很麻煩的,可以使用生成器函數(shù)去替代迭代器的功能,所以上面的代碼可以使用生成器函數(shù)改造如下:

function* primeGenerator () {
  let prime = 1
  while (true) {
    prime++
    if (isPrime(prime)) {
      yield prime
    }
  }
}

var getPrime = primeGenerator().next().value
console.log(getPrime());	// 2
console.log(getPrime());	// 3

4. 小結(jié)

本節(jié)我們主要學(xué)習(xí)了生成器的概念和用法,需要生成器對象是由生成器函數(shù)返回的結(jié)果,生成器對象是遵守迭代協(xié)議和迭代器協(xié)議實(shí)現(xiàn)的 Iterable 接口。生成器其實(shí)就是對迭代器的應(yīng)用。另外,通過兩個案例更加深刻地理解了生成器的應(yīng)用場景,對比了生成器和迭代器的不同。