ES6迭代器的簡單指南和示例

圖片描述

咱們將在本文中分析迭代器。迭代器是在JavaScript中循環任何集合的一種新方法。它們是在ES6中引入的,因爲它們的普遍用途和在不一樣地方的使用而變得很是流行。html

咱們將從概念上理解迭代器是什麼,以及在何處使用它們和示例。咱們還將看到它在JavaScript中的一些實現。若是我問你,你會怎麼作?你會說——很簡單。我將使用 forwhilefor-of其它 方法對它們進行循環。前端

想閱讀更多優質文章請猛戳GitHub博客,一年百來篇優質文章等着你!git

簡介

假設你有這個數組es6

const myFavouriteAuthors = [
  'Neal Stephenson',
  'Arthur Clarke',
  'Isaac Asimov', 
  'Robert Heinlein'
];

在某些狀況下,但願返回數組中的全部單獨值,以便在屏幕上打印它們、操做它們或對它們執行某些操做。github

以下:數組

圖片描述

如今,假設你擁有一個自定義數據結構來保存全部做者,而不是上面的數組,如:數據結構

圖片描述

mypreferteauthors 是一個對象,它包含另外一個對象 allAuthors。allAuthors 包含三個數組,其中包含 fiction、scienceFiction 和 fantasy。ide

如今,若是要求你循環遍歷 myFavouriteAuthors 以得到全部的做者,你的方法是什麼? 你可能會嘗試一些循環組合來得到全部數據。函數

可是,若是你這樣作了 ——oop

for (let author of myFavouriteAuthors) { 
  console.log(author)
}
// TypeError: {} is not iterable

你將獲得一個類型錯誤,說明該對象不可迭代。讓咱們看看什麼是可迭代的,以及如何使對象可迭代。

在本文的最後,你將瞭解如何在定製對象上使用for-of循環,在本例中是在 mypreferteauthors 上使用 for-of 循環。

可迭代對象與迭代器 (Iterables and Iterators)

在上一節中看到了問題,從咱們的自定義對象中獲取全部的做者是不容易的。咱們須要某種方法,經過它咱們能夠有序地獲取內部數據。

咱們在 mypreferteauthors 中添加一個返回全部做者的方法 getAllAuthors。如:

圖片描述

這是一個簡單的方法。它幫咱們完成了獲取全部做者的功能。可是,這種實現可能會出現一些問題:

  • getAllAuthors 的名稱很是具體。若是其餘人正在建立本身的 mypreferteauthors,他們可能會將其命名爲retrieveAllAuthors。
  • 做爲開發人員,咱們老是須要知道返回全部數據的特定方法,在本例中,它被命名爲getAllAuthors。
  • getAllAuthors 返回的是字符串數組,若是另外一個開發人員以這種格式返回一個對象數組,該怎麼辦:
[ {name: 'Agatha Christie'}, {name: 'J. K. Rowling'}, ... ]

開發人員必須知道返回全部數據的方法的確切名稱和返回類型。

若是咱們規定方法的名稱和它的返回類型是固定不變的呢?

讓咱們將這個方法命名爲 --- iteratorMethod

ECMA 也採起了相似的步驟來標準化在定製對象上循環的過程。可是,ECMA沒有使用名稱 iteratorMethod,而是使用名稱 Symbol.iteratorSymbols 提供的名稱是惟一的,不能與其餘屬性名稱衝突。同時,Symbol.iterator 返回一個名爲迭代器的對象,這個迭代器將擁有一個名爲next的方法,該方法將返回一個具備鍵值爲 valuedone 的對象。

值鍵 value 包含當前值,它能夠是任何類型的,done 是布爾值,它表示是否獲取了全部的值。

下圖能夠幫助創建可迭代對象、迭代器和next之間的關係,這種關係稱爲迭代協議

圖片描述

根據Axel Rauschmayer博士的《探索JS》一書:

  • 可迭代是一種數據結構,它但願使其元素對外部可訪問,經過實現一個關鍵字是Symbol.iterator的方法來實現,該方法是迭代器的工廠,也就是說,它將建立迭代器。
  • 迭代器是一個指針,用於遍歷數據結構的元素,咱們將使用computed property語法來設置這個鍵,以下:

創建可迭代對象

所以,正如咱們在上一節學到的,咱們須要實現一個名爲Symbol.iterator的方法

圖片描述

在第4行,咱們建立迭代器。它是一個定義了next方法的對象。next方法根據step變量返回值。在第25行,咱們檢索iterator,27行,咱們調用next方法,直到 done的值爲 true。

這正是for-of循環中發生的事情,for-of接受一個迭代器,並建立它的迭代器,它會一直調用next(),直到 done爲 true。

JavaScript中可迭代對象(iterable)

JavaScript中的不少對象都是可迭代的。它們可能不是很好的察覺,可是若是仔細檢查,就會發現迭代的特徵:

  • Arrays and TypedArrays
  • Strings —— 遍歷每一個字符或Unicode代碼點
  • Maps —— 遍歷其鍵-值對
  • Sets —— 遍歷元素
  • arguments  —— 函數中相似數組的特殊變量
  • DOM elements (Work in Progress)

JS中使用迭代的其餘一些結構是:

  • for-of -- for-of 循環須要一個可迭代的對象,不然,它將拋出一個類型錯誤。
for (const value of iterable) { ... }
  • 數組解構 -- 因爲可迭代性,會發生析構。讓咱們來看看:

    const array = ['a', 'b', 'c', 'd', 'e'];
    const [first, ,third, ,last] = array;

等價於:

const array = ['a', 'b', 'c', 'd', 'e'];
const iterator = array[Symbol.iterator]();
const first = iterator.next().value
iterator.next().value // Since it was skipped, so it's not assigned
const third = iterator.next().value
iterator.next().value // Since it was skipped, so it's not assigned
const last = iterator.next().value
  • 擴展操做符(…)
const array = ['a', 'b', 'c', 'd', 'e'];

const newArray = [1, ...array, 2, 3];

等價於:

const array = ['a', 'b', 'c', 'd', 'e'];
const iterator = array[Symbol.iterator]();
const newArray = [1];
for (let nextValue = iterator.next(); nextValue.done !== true; nextValue = iterator.next()) {
  newArray.push(nextValue.value);
}
newArray.push(2)
newArray.push(3)
  • Promise.allPromise.race 接受可迭代對象
  • Maps 和 Sets

讓 myFavouriteAuthors 可迭代

下面是一個實現,它使mypreferteauthors具備可迭代性:

const myFavouriteAuthors = {
  allAuthors: {
    fiction: [
      'Agatha Christie', 
      'J. K. Rowling',
      'Dr. Seuss'
    ],
    scienceFiction: [
      'Neal Stephenson',
      'Arthur Clarke',
      'Isaac Asimov', 
      'Robert Heinlein'
    ],
    fantasy: [
      'J. R. R. Tolkien',
      'J. K. Rowling',
      'Terry Pratchett'
    ],
  },
  [Symbol.iterator]() {
    // 獲取數組中的全部做者
    const genres = Object.values(this.allAuthors);
    
    // 存儲當前類型和索引
    let currentAuthorIndex = 0;
    let currentGenreIndex = 0;
    
    return {
      // Implementation of next()
      next() {
        // 根據當前的索引獲取對應的做者信息
        const authors = genres[currentGenreIndex];
        
        // 當遍歷完數組 authors是,oNotHaveMoreAuthors 爲 true
        const doNothaveMoreAuthors = !(currentAuthorIndex < authors.length);
        if (doNothaveMoreAuthors) {
         // 加一繼續訪問下一個
          currentGenreIndex++;
          // 重置
          currentAuthorIndex = 0;
        }
        
        // 若是全部 genres 都遍歷完告終,那麼咱們須要告訴迭代器不能提供更多的值。
        const doNotHaveMoreGenres = !(currentGenreIndex < genres.length);
        if (doNotHaveMoreGenres) {
          return {
            value: undefined,
            done: true
          };
        }
        
        // 若是一切正常,從當genre 返回 做者和當前做者索引,以便下次,下一個做者能夠返回。
        return {
          value: genres[currentGenreIndex][currentAuthorIndex++],
          done: false
        }
      }
    };
  }
};

for (const author of myFavouriteAuthors) {
  console.log(author);
}

console.log(...myFavouriteAuthors)

經過本文得到的知識,你能夠很容易地理解迭代器是如何工做的,這種邏輯可能有點難以理解。所以,理解這個概念的最佳方法是多多敲死代碼,多多驗證!

你的點贊是我持續分享好東西的動力,歡迎點贊!

歡迎加入前端你們庭,裏面會常常分享一些技術資源。

圖片描述

相關文章
相關標籤/搜索