JavaScript迭代與迭代器(Iterable and Iterator)

fu4723 9年前發布 | 11K 次閱讀 JavaScript開發 JavaScript

我們在很前面的時候就講到了迭代器這么一個東西。那么他究竟是什么呢?又有什么樣的作用呢?本節我們就來講述 Iterables 與 Iterators。也就是可迭代性與迭代器。

概述

ES6 中新增了一個迭代的接口,叫做可迭代性(Iterable )。本節呢,將要向大家講述它是怎么工作的,以及它運用于那些 ECMAScript 語言類型中。

我們來看看迭代(Iteration )是什么,它分為兩個部分:

  • Iterable: 可迭代性是一種數據結構,它希望使其元素可以訪問公共部分。它通過內置系統的一個方法,鍵為 Symbol.iterator。這個方法就是迭代器的工廠。
  • Iterator: 用于遍歷數據結構的元素的指針。

以下的值具有可迭代性:

  • Array
  • String
  • Map
  • Set
  • DOM 數據結構(在程序中工作的那部分)

那么具有可迭代性的對象可以做哪些事呢?如下所示:

(1)解構

let [a,b] = new Set(['a', 'b']);
console.log(a);  // "a"
console.log(b);  // "b"

(2)for-of 循環

for (let x of ['a', 'b', 'c']) {
    console.log(x);
} 
// "a"
// "b"
// "c"

(3)Array.from( ) 方法

let arr = Array.from(new Set(['a', 'b', 'c']));
//  ["a", "b", "c"]

(4)展開操作符(...)

let arr = [...new Set(['a', 'b', 'c'])]; 
// ["a", "b", "c"]

(5)Map 和 Set 構造函數

let map = new Map([
    [1,'a'],
    [2,'b']
]);

let set = new Set(['a', 'b', 'c']);

(6)Promise.all( ) 、Promise.race( ) 方法

Promise.all(iterableOverPromises).then(···);
Promise.race(iterableOverPromises).then(···);

(7)yeild(*)

yield* anIterable;

看完以上的這些,記住了,基本上這節內容就大致清楚了。

可迭代性

這里我們將要講到可迭代性的思想,也就是說,可迭代到底是什么?

可迭代性的思想:

  • 數據消費者( Data consumers ):JavaScript 具有“消費”數據的語言結構。例如,for-of 循環用于遍歷值,而擴展運算符(...)將值插入到數組或函數調用中。
  • 數據源( Data Sources ):數據消費者可以從各種來源獲取值。例如,您可能需要遍歷數組的元素,或者Map 中的鍵值(使用 entries 方法)以及 String 實例中的字符等。

我們這里必須清楚,想要每個數據消費者都能獲取數據源,這是不切實際的。尤其是當我們創建新的源時(例如通過一個庫)。因此,ES6 就提供了 Iterable 的接口。數據消費者使用它,而數據源負責實現它。我們來看下圖:

迭代關系

這里有兩個知識:

  • 源( Source ):如果一個鍵擁有 Symbol.Iterator 方法并返回迭代器時,它的值就被認為是可迭代的。迭代器是一個對象,它可以使用 next( ) 方法返回值。我們可以這樣說:這個方法每次使用時,都可以枚舉值。
  • 消耗( Consumption ):數據使用者使用迭代器來檢索它們消耗的值。

這里我們來舉個之前的例子吧:

let set = new Set(['a','b','c']);
let keys = set.keys();
console.log(keys.next());  // {value: "a", done: false}
console.log(keys.next());  //  {value: "b", done: false}
console.log(keys.next());  // {value: "c", done: false}
console.log(keys.next());  // {value: undefined, done: true}

上述示例中,我們使用了 keys() 方法與 next() 方法來進行該 Set 實例每個值的枚舉與輸出。就是因為 Set 擁有 Symbol.Iterator 方法。因此我們還可以使用下述的方式進行遍歷:

let set = new Set(['a','b','c']);
let iter = set[Symbol.iterator]();
console.log(iter.next());  // {value: "a", done: false}
console.log(iter.next());  //  {value: "b", done: false}
console.log(iter.next());  // {value: "c", done: false}
console.log(iter.next());  // {value: undefined, done: true}

輸出結果一致,沒有問題。

可以看到,next( ) 返回包裝在對象中的每個項目,作為屬性值的值。布爾屬性 done 指示何時已達到項目序列的結束。

迭代和迭代器是所謂的協議(方法加上使用它們的規則)的一部分迭代。此協議的關鍵特性是它是順序的:迭代器一次返回一個值。 這意味著如果一個可迭代的數據結構是非線性的(如一顆樹),迭代將會使其線性化。

迭代的數據源

在概述中我舉了幾個例子。以下我使用 for-of 循環來迭代每個類型。我們一起來看下輸出的值是什么:

(1)Array

for (let x of ['a', 'b']) {
    console.log(x);
} 
// "a"
// "b"

(2)String

for (let x of 'Hello') {
    console.log(x);
}
// "H"
// "e"
// "l"
// "l"
// "e"

(3)Map

let map = new Map()
    .set('a', 1)
    .set('b', 2);
for (let pair of map) {
    console.log(pair);
}
// ["a", 1]
// ["b", 2]

(4)Set

let set = new Set()
    .add('a')
    .add('b');
for (let x of set) {
    console.log(x);
}
// "a"
// "b"

(5)arguments

function printArgs() {
    for (let x of arguments) {
        console.log(x);
    }
}
printArgs('a', 'b'); 
// "a"
// "b"

(6)DOM 數據結構

for (let node of document.querySelectorAll('div')) {
    // ···
}

現在更加清晰了吧?舉出這么多例子的目的就是希望大家熟悉它。

迭代計算的數據

不是所有可迭代內容都必須來自數據結構,它也可以即時計算。

例如我們前面學習的 Array、Map、Set 都擁有 entries( )、keys( )、values( ) 三個方法。它們都是即時計算實例中的內容,再進行輸出。

  • entries( ) 方法返回實例的 [key, value] 的數組。
  • keys( ) 方法返回實例的鍵。
  • values( ) 方法返回實例的值。

但是,我們需要知道,Object 類型是沒有迭代性可言的。因此它沒有 for-of 循環,只有 for-in 的遍歷。當然,在未來可能會有內置方法。

// 錯誤
for (let x of {a: 1, b: 2}) { // TypeError
    console.log(x);
}

總結

本節把迭代的知識點講完了,但是具體的用法還是比較多的。需要我們不斷地實踐。當你忘記的時候,就回來看看,將這些可迭代的對象牢記在腦海中,在使用時,就不會出錯。

 

來自:http://www.jianshu.com/p/2d0187f30a54

 

 本文由用戶 fu4723 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!