首頁  >  文章  >  web前端  >  ES6可迭代協定和迭代器協定詳解

ES6可迭代協定和迭代器協定詳解

小云云
小云云原創
2018-01-15 09:10:071503瀏覽

本文主要介紹了詳解ES6語法之可迭代協議和迭代器協議,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟著小編過來看看吧,希望能幫助大家。

ECMAScript 2015的幾個補充,並不是新的內建或語法,而是協議。這些協議可以被任何遵循某些約定的物件來實現。
有兩個協定:可迭代協定和迭代器協定。

可迭代協定

可迭代協定允許JavaScript 物件去定義或自訂它們的迭代行為, 例如(定義)在一個for. .of 結構中什麼值可以被循環(得到)。有些內建類型都是內建的可迭代物件並且有預設的迭代行為, 例如 Array or Map, 另一些類型則不是 (比如Object) 。

Iterator 介面的目的,就是為所有資料結構,提供了一個統一的存取機制,即for...of循環(詳見下文)。當使用for...of循環遍歷某種資料結構時,該循環會自動去尋找 Iterator 接口,呼叫Symbol.iterator方法,傳回該物件的預設遍歷器。

ES6 規定,預設的 Iterator 介面部署在資料結構的Symbol.iterator屬性,或者說,一個資料結構只要具有Symbol.iterator屬性,就可以認為是「可迭代的」(iterable)。 Symbol.iterator屬性本身就是函數,就是目前資料結構預設的遍歷器產生函數。執行這個函數,就會回傳一個遍歷器。

為了變成可迭代對象, 一個物件必須實作(或它原型鏈的某個物件)必須有一個名字是Symbol.iterator 的屬性:

迭代器協議

此迭代器協定定義了一種標準的方式來產生一個有限或無限序列的值。

JavaScript 原有的表示「集合」的資料結構,主要是陣列(Array)和物件(Object),ES6 又加入了Map和Set。這樣就有了四種資料集合,使用者還可以組合使用它們,定義自己的資料結構,例如陣列的成員是Map,Map的成員是物件。這樣就需要一個統一的介面機制,來處理所有不同的資料結構。

迭代器(Iterator)就是這樣一種機制。它是一種接口,為各種不同的資料結構提供統一的存取機制。任何資料結構只要部署 Iterator 接口,就可以完成遍歷操作(即依序處理該資料結構的所有成員)。

Iterator 的作用有三:一是為各種資料結構,提供一個統一的、簡單的存取介面;二是使得資料結構的成員能夠依某種次序排列;三是ES6 創造了一種新的遍歷指令for...of循環,Iterator 介面主要供for...of消費。

Iterator 的遍歷過程是這樣的。

  1. 建立一個指針對象,指向目前資料結構的起始位置。也就是說,遍歷器物件本質上,就是一個指針對象。

  2. 第一次呼叫指標針對象的next方法,可以將指標指向資料結構的第一個成員。

  3. 第二次呼叫指標針對象的next方法,指標就指向資料結構的第二個成員。

  4. 不斷呼叫指標針對象的next方法,直到它指向資料結構的結束位置。

每次呼叫next方法,都會傳回資料結構的目前成員的資訊。具體來說,就是傳回一個包含value和done兩個屬性的物件。其中,value屬性是目前成員的值,done屬性是一個布林值,表示遍歷是否結束。


var someString = "hi";
typeof someString[Symbol.iterator]; // "function"
var iterator = someString[Symbol.iterator]();
iterator + "";  // "[object String Iterator]"
iterator.next()    // { value: "h", done: false }
iterator.next();   // { value: "i", done: false }
iterator.next();   // { value: undefined, done: true }

原生具備 Iterator 介面的資料結構如下。

  1. Array

  2. Map

  3. Set

  4. String

  5. TypedArray

  6. 函數的arguments 物件

  7. NodeList 物件

#注意物件是不具備Iterator 接口的,一個物件如果要具備可被for...of循環調用的Iterator 接口,就必須在Symbol.iterator的屬性上部署遍歷器生成方法(原型鏈上的物件具有該方法也可)。

呼叫Iterator 介面的場合

有一些場合會預設呼叫Iterator 介面(即Symbol.iterator方法),除了下文會介紹的for.. .of循環,解構賦值, 擴充運算子其實也會呼叫預設的Iterator 介面。

實際上,這提供了一種簡單機制,可以將任何部署了 Iterator 介面的資料結構,轉為陣列。也就是說,只要某個資料結構部署了 Iterator 接口,就可以對它使用擴充運算符,將其轉為數組。

由於數組的遍歷會呼叫遍歷器接口,所以任何接受數組作為參數的場合,其實都呼叫了遍歷器接口。下面是一些例子。

  1. for...of

  2. Array.from()

  3. Map(), Set(), WeakMap(), WeakSet()(如new Map([['a',1],['b',2]]))

  4. Promise.all ()

  5. Promise.race()

for...of

for...of 循环是最新添加到 JavaScript 循环系列中的循环。

它结合了其兄弟循环形式 for 循环和 for...in 循环的优势,可以循环任何可迭代(也就是遵守可迭代协议)类型的数据。默认情况下,包含以下数据类型:String、Array、Map 和 Set,注意不包含 Object 数据类型(即 {})。默认情况下,对象不可迭代。

在研究 for...of 循环之前,先快速了解下其他 for 循环,看看它们有哪些不足之处。

for 循环

for 循环的最大缺点是需要跟踪计数器和退出条件。我们使用变量 i 作为计数器来跟踪循环并访问数组中的值。我们还使用 Array.length 来判断循环的退出条件。

虽然 for 循环在循环数组时的确具有优势,但是某些数据结构不是数组,因此并非始终适合使用 loop 循环。

for...in 循环

for...in 循环改善了 for 循环的不足之处,它消除了计数器逻辑和退出条件。但是依然需要使用 index 来访问数组的值.

此外,当你需要向数组中添加额外的方法(或另一个对象)时,for...in 循环会带来很大的麻烦。因为 for...in 循环循环访问所有可枚举的属性,意味着如果向数组的原型中添加任何其他属性,这些属性也会出现在循环中。这就是为何在循环访问数组时,不建议使用 for...in 循环。

注意: forEach 循环 是另一种形式的 JavaScript 循环。但是,forEach() 实际上是数组方法,因此只能用在数组中。也无法停止或退出 forEach 循环。如果希望你的循环中出现这种行为,则需要使用基本的 for 循环。

for...of 循环

for...of 循环用于循环访问任何可迭代的数据类型。


const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

for (const digit of digits) {
 console.log(digit);
}

可以随时停止或退出 for...of 循环。


const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

for (const digit of digits) {
 if (digit % 2 === 0) {
  continue;
 }
 console.log(digit); //1,3,5,7,9
}

不用担心向对象中添加新的属性。for...of 循环将只循环访问对象中的值。

相关推荐:

实例分享PHP迭代器接口Iterator用法分析

调整JavaScript抽象的迭代方案

JavaScript数组的5种迭代方法实例详解


以上是ES6可迭代協定和迭代器協定詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn