ES6迭代器和生成器

1、迭代器

JavaScript 原有的表示「集合」的數據結構,主要是數組(Array)和對象(Object),ES6 又添加了Map和Set。這樣就須要一種統一的接口機制,來處理全部不一樣的數據結構。遍歷器(Iterator)就是這樣一種機制。它是一種接口,爲各類不一樣的數據結構提供統一的訪問機制。任何數據結構只要部署 Iterator 接口,就能夠完成遍歷操做(即依次處理該數據結構的全部成員)html

1.Iterator的做用:

  • 爲各類數據結構,提供一個統一的、簡便的訪問接口;
  • 使得數據結構的成員可以按某種次序排列
  • ES6創造了一種新的遍歷命令for...of循環,Iterator接口主要供for...of消費。

2.原生具有iterator接口的數據(可用for of遍歷)

  • Array
  • set容器
  • map容器
  • String
  • 函數的 arguments 對象
  • NodeList 對象
let arr3 = [1, 2, 'kobe', true];
for(let i of arr3){
   console.log(i); // 1 2 kobe true
}
複製代碼
let str = 'abcd';
for(let item of str){
   console.log(item); // a b c d
}   
複製代碼
function fun() {
    for (let i of arguments) {
       console.log(i) // 1 4 5
    }
}
fun(1, 4, 5)
複製代碼
var engines = new Set(["Gecko", "Trident", "Webkit", "Webkit"]);
for (var e of engines) {
  console.log(e);
}
// Gecko
// Trident
// Webkit    
複製代碼

3.迭代器的工做原理

  • 建立一個指針對象,指向數據結構的起始位置。
  • 第一次調用next方法,指針自動指向數據結構的第一個成員
  • 接下來不斷調用next方法,指針會一直日後移動,直到指向最後一個成員
  • 每調用next方法返回的是一個包含value和done的對象,{value: 當前成員的值,done: 布爾值}
    • value表示當前成員的值,done對應的布爾值表示當前的數據的結構是否遍歷結束。
    • 當遍歷結束的時候返回的value值是undefined,done值爲true

4.手寫一個迭代器

function myIterator(arr) {
        let nextIndex = 0
        return {
          next: function() {
            return nextIndex < arr.length
              ? { value: arr[nextIndex++], done: false }
              : { value: undefined, value: true }
          }
        }
      }
      let arr = [1, 4, 'ads']// 準備一個數據
      let iteratorObj = myIterator(arr)
      console.log(iteratorObj.next()) // 全部的迭代器對象都擁有next()方法,會返回一個結果對象
      console.log(iteratorObj.next())
      console.log(iteratorObj.next())
      console.log(iteratorObj.next())
複製代碼

5.注意點

① for of循環不支持遍歷普通對象前端

var obj = { a: 2, b: 3 }
   for (let i of obj) {
     console.log(i) // Uncaught TypeError: obj is not iterable
}
複製代碼

對象的Symbol.iterator屬性,指向該對象的默認遍歷器方法。當使用for of去遍歷某一個數據結構的時候,首先去找Symbol.iterator,找到了就去遍歷,沒有找到的話不能遍歷,提示Uncaught TypeError: XXX is not iterablegit

② 當使用擴展運算符(...)或者對數組和 Set 結構進行解構賦值時,會默認調用Symbol.iterator方法es6

let arr1 = [1,3]
let arr2 = [2,3,4,5]
arr2 = [1,...arr2,6]
console.log(arr2) // [1, 2, 3, 4, 5, 6]
複製代碼

2、生成器

1.概念

  • Generator 函數是 ES6 提供的一種異步編程解決方案,語法行爲與傳統函數徹底不一樣
  • 語法上,首先能夠把它理解成,Generator 函數是一個狀態機,封裝了多個內部狀態
  • Generator 函數除了狀態機,仍是一個遍歷器對象生成函數
  • 可暫停函數(惰性求值), yield可暫停,next方法可啓動。每次返回的是yield後的表達式結果

2.特色

  • function關鍵字與函數名之間有一個星號;
  • 函數體內部使用yield表達式,定義不一樣的內部狀態
function* generatorExample(){
    console.log("開始執行")
    yield 'hello';  
    yield 'generator'; 
 }
// generatorExample() 
// 這種調用方法Generator 函數並不會執行
let MG = generatorExample() // 返回指針對象
MG.next() //開始執行  {value: "hello", done: false}
複製代碼

Generator 函數是分段執行的,調用next方法函數內部邏輯開始執行,遇到yield表達式中止,返回{value: yield後的表達式結果/undefined, done: false/true},再次調用next方法會從上一次中止時的yield處開始,直到最後。github

function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}
var hw = helloWorldGenerator();
hw.next()// { value: 'hello', done: false }
hw.next()// { value: 'world', done: false }
hw.next()// { value: 'ending', done: true }
hw.next()// { value: undefined, done: true }
複製代碼

第一次調用,Generator 函數開始執行,直到遇到第一個yield表達式爲止。next方法返回一個對象,它的value屬性就是當前yield表達式的值hello,done屬性的值false,表示遍歷尚未結束。ajax

第二次調用,Generator 函數從上次yield表達式停下的地方,一直執行到下一個yield表達式。next方法返回的對象的value屬性就是當前yield表達式的值world,done屬性的值false,表示遍歷尚未結束。編程

第三次調用,Generator 函數從上次yield表達式停下的地方,一直執行到return語句(若是沒有return語句,就執行到函數結束)。next方法返回的對象的value屬性,就是緊跟在return語句後面的表達式的值(若是沒有return語句,則value屬性的值爲undefined),done屬性的值true,表示遍歷已經結束。數組

第四次調用,此時 Generator 函數已經運行完畢,next方法返回對象的value屬性爲undefined,done屬性爲true。之後再調用next方法,返回的都是這個值。bash

3.next傳遞參數

yield表達式自己沒有返回值,或者說老是返回undefined。next方法能夠帶一個參數,該參數就會被看成上一個yield表達式的返回值。數據結構

function* generatorExample () {
  console.log('開始執行')
  let result = yield 'hello'
  console.log(result)
  yield 'generator'
}
let MG = generatorExample()
MG.next()
MG.next()
// 開始執行
// undefined
// {value: "generator", done: false}
複製代碼

沒有傳值時result默認是undefined,接下來咱們向第二個next傳遞一個參數,看下輸出結果是啥?

function* generatorExample () {
  console.log('開始執行')
  let result = yield 'hello'
  console.log(result)
  yield 'generator'
}
let MG = generatorExample()
MG.next()
MG.next(11)
// 開始執行
// 11
// {value: "generator", done: false}
複製代碼

4.與 Iterator 接口的關係

咱們上文中提到對象沒有iterator接口,用for...of遍歷時便會報錯。

let obj = { username: 'kobe', age: 39 }
for (let i of obj) {
  console.log(i) //  Uncaught TypeError: obj is not iterable
}
複製代碼

因爲 Generator 函數就是遍歷器生成函數,所以能夠把 Generator 賦值給對象的Symbol.iterator屬性,從而使得該對象具備 Iterator 接口

let obj = { username: 'kobe', age: 39 }
obj[Symbol.iterator] = function* myTest() {
  yield 1;
  yield 2;
  yield 3;
};
for (let i of obj) {
  console.log(i) // 1 2 3
}
複製代碼

上面代碼中,Generator函數賦值給Symbol.iterator屬性,從而使得obj對象具備了 Iterator 接口,能夠被for of遍歷了。

5.Generator的異步的應用

業務需求:

  • 發送ajax請求獲取新聞內容
  • 新聞內容獲取成功後再次發送請求,獲取對應的新聞評論內容
  • 新聞內容獲取失敗則不須要再次發送請求。

如何實現(前端核心代碼以下):

function* sendXml() {
      // url爲next傳參進來的數據
     let url = yield getNews('http://localhost:3000/news?newsId=2');//獲取新聞內容
      yield getNews(url);//獲取對應的新聞評論內容,只有先獲取新聞的數據拼湊成url,才能向後臺請求
    }
    function getNews(url) {
      $.get(url, function (data) {
        console.log(data);
        let commentsUrl = data.commentsUrl;
        let url = 'http://localhost:3000' + commentsUrl;
        // 當獲取新聞內容成功,發送請求獲取對應的評論內容
        // 調用next傳參會做爲上次暫停是yield的返回值
        sx.next(url);
      })
    }
    let sx = sendXml();// 發送請求獲取新聞內容
    sx.next();
複製代碼

若是以爲文章對你有些許幫助,歡迎在個人GitHub博客點贊和關注,感激涕零!

參考文章

ECMAScript 6 入門

進階 Javascript 生成器

ES6生成器函數generator

相關文章
相關標籤/搜索