詳解ES6語法之可迭代協(xié)議和迭代器協(xié)議
ECMAScript 2015的幾個補充,并不是新的內(nèi)置或語法,而是協(xié)議。這些協(xié)議可以被任何遵循某些約定的對象來實現(xiàn)。
有兩個協(xié)議:可迭代協(xié)議和迭代器協(xié)議。
可迭代協(xié)議
可迭代協(xié)議允許 JavaScript 對象去定義或定制它們的迭代行為, 例如(定義)在一個 for..of 結(jié)構(gòu)中什么值可以被循環(huán)(得到)。一些內(nèi)置類型都是內(nèi)置的可迭代對象并且有默認的迭代行為, 比如 Array or Map, 另一些類型則不是 (比如Object) 。
Iterator 接口的目的,就是為所有數(shù)據(jù)結(jié)構(gòu),提供了一種統(tǒng)一的訪問機制,即for...of循環(huán)(詳見下文)。當使用for...of循環(huán)遍歷某種數(shù)據(jù)結(jié)構(gòu)時,該循環(huán)會自動去尋找 Iterator 接口,調(diào)用Symbol.iterator方法,返回該對象的默認遍歷器。
ES6 規(guī)定,默認的 Iterator 接口部署在數(shù)據(jù)結(jié)構(gòu)的Symbol.iterator屬性,或者說,一個數(shù)據(jù)結(jié)構(gòu)只要具有Symbol.iterator屬性,就可以認為是“可迭代的”(iterable)。Symbol.iterator屬性本身是一個函數(shù),就是當前數(shù)據(jù)結(jié)構(gòu)默認的遍歷器生成函數(shù)。執(zhí)行這個函數(shù),就會返回一個遍歷器。
為了變成可迭代對象, 一個對象必須實現(xiàn)(或者它原型鏈的某個對象)必須有一個名字是 Symbol.iterator 的屬性:
迭代器協(xié)議
該迭代器協(xié)議定義了一種標準的方式來產(chǎn)生一個有限或無限序列的值。
JavaScript 原有的表示“集合”的數(shù)據(jù)結(jié)構(gòu),主要是數(shù)組(Array)和對象(Object),ES6 又添加了Map和Set。這樣就有了四種數(shù)據(jù)集合,用戶還可以組合使用它們,定義自己的數(shù)據(jù)結(jié)構(gòu),比如數(shù)組的成員是Map,Map的成員是對象。這樣就需要一種統(tǒng)一的接口機制,來處理所有不同的數(shù)據(jù)結(jié)構(gòu)。
迭代器(Iterator)就是這樣一種機制。它是一種接口,為各種不同的數(shù)據(jù)結(jié)構(gòu)提供統(tǒng)一的訪問機制。任何數(shù)據(jù)結(jié)構(gòu)只要部署 Iterator 接口,就可以完成遍歷操作(即依次處理該數(shù)據(jù)結(jié)構(gòu)的所有成員)。
Iterator 的作用有三個:一是為各種數(shù)據(jù)結(jié)構(gòu),提供一個統(tǒng)一的、簡便的訪問接口;二是使得數(shù)據(jù)結(jié)構(gòu)的成員能夠按某種次序排列;三是 ES6 創(chuàng)造了一種新的遍歷命令for...of循環(huán),Iterator 接口主要供for...of消費。
Iterator 的遍歷過程是這樣的。
- 創(chuàng)建一個指針對象,指向當前數(shù)據(jù)結(jié)構(gòu)的起始位置。也就是說,遍歷器對象本質(zhì)上,就是一個指針對象。
- 第一次調(diào)用指針對象的next方法,可以將指針指向數(shù)據(jù)結(jié)構(gòu)的第一個成員。
- 第二次調(diào)用指針對象的next方法,指針就指向數(shù)據(jù)結(jié)構(gòu)的第二個成員。
- 不斷調(diào)用指針對象的next方法,直到它指向數(shù)據(jù)結(jié)構(gòu)的結(jié)束位置。
每一次調(diào)用next方法,都會返回數(shù)據(jù)結(jié)構(gòu)的當前成員的信息。具體來說,就是返回一個包含value和done兩個屬性的對象。其中,value屬性是當前成員的值,done屬性是一個布爾值,表示遍歷是否結(jié)束。
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 接口的數(shù)據(jù)結(jié)構(gòu)如下。
- Array
- Map
- Set
- String
- TypedArray
- 函數(shù)的 arguments 對象
- NodeList 對象
注意對象是不具備 Iterator 接口的,一個對象如果要具備可被for...of循環(huán)調(diào)用的 Iterator 接口,就必須在Symbol.iterator的屬性上部署遍歷器生成方法(原型鏈上的對象具有該方法也可)。
調(diào)用 Iterator 接口的場合
有一些場合會默認調(diào)用 Iterator 接口(即Symbol.iterator方法),除了下文會介紹的for...of循環(huán),解構(gòu)賦值, 擴展運算符其實也會調(diào)用默認的Iterator 接口。
實際上,這提供了一種簡便機制,可以將任何部署了 Iterator 接口的數(shù)據(jù)結(jié)構(gòu),轉(zhuǎn)為數(shù)組。也就是說,只要某個數(shù)據(jù)結(jié)構(gòu)部署了 Iterator 接口,就可以對它使用擴展運算符,將其轉(zhuǎn)為數(shù)組。
由于數(shù)組的遍歷會調(diào)用遍歷器接口,所以任何接受數(shù)組作為參數(shù)的場合,其實都調(diào)用了遍歷器接口。下面是一些例子。
- for...of
- Array.from()
- Map(), Set(), WeakMap(), WeakSet()(比如new Map([['a',1],['b',2]]))
- Promise.all()
- Promise.race()
for...of
for...of 循環(huán)是最新添加到 JavaScript 循環(huán)系列中的循環(huán)。
它結(jié)合了其兄弟循環(huán)形式 for 循環(huán)和 for...in 循環(huán)的優(yōu)勢,可以循環(huán)任何可迭代(也就是遵守可迭代協(xié)議)類型的數(shù)據(jù)。默認情況下,包含以下數(shù)據(jù)類型:String、Array、Map 和 Set,注意不包含 Object 數(shù)據(jù)類型(即 {})。默認情況下,對象不可迭代。
在研究 for...of 循環(huán)之前,先快速了解下其他 for 循環(huán),看看它們有哪些不足之處。
for 循環(huán)
for 循環(huán)的最大缺點是需要跟蹤計數(shù)器和退出條件。我們使用變量 i 作為計數(shù)器來跟蹤循環(huán)并訪問數(shù)組中的值。我們還使用 Array.length 來判斷循環(huán)的退出條件。
雖然 for 循環(huán)在循環(huán)數(shù)組時的確具有優(yōu)勢,但是某些數(shù)據(jù)結(jié)構(gòu)不是數(shù)組,因此并非始終適合使用 loop 循環(huán)。
for...in 循環(huán)
for...in 循環(huán)改善了 for 循環(huán)的不足之處,它消除了計數(shù)器邏輯和退出條件。但是依然需要使用 index 來訪問數(shù)組的值.
此外,當你需要向數(shù)組中添加額外的方法(或另一個對象)時,for...in 循環(huán)會帶來很大的麻煩。因為 for...in 循環(huán)循環(huán)訪問所有可枚舉的屬性,意味著如果向數(shù)組的原型中添加任何其他屬性,這些屬性也會出現(xiàn)在循環(huán)中。這就是為何在循環(huán)訪問數(shù)組時,不建議使用 for...in 循環(huán)。
注意: forEach 循環(huán) 是另一種形式的 JavaScript 循環(huán)。但是,forEach() 實際上是數(shù)組方法,因此只能用在數(shù)組中。也無法停止或退出 forEach 循環(huán)。如果希望你的循環(huán)中出現(xiàn)這種行為,則需要使用基本的 for 循環(huán)。
for...of 循環(huán)
for...of 循環(huán)用于循環(huán)訪問任何可迭代的數(shù)據(jù)類型。
const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; for (const digit of digits) { console.log(digit); }
可以隨時停止或退出 for...of 循環(huán)。
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 }
不用擔心向?qū)ο笾刑砑有碌膶傩?。for...of 循環(huán)將只循環(huán)訪問對象中的值。
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
- 不得不知的ES6小技巧
- 你點的 ES6一些小技巧,請查收
- 淺談讓你的代碼更簡短,更整潔,更易讀的ES6小技巧
- ES6小技巧之代替lodash
- JS實現(xiàn)集合的交集、補集、差集、去重運算示例【ES5與ES6寫法】
- ES6 迭代器(Iterator)和 for.of循環(huán)使用方法學習(總結(jié))
- 深入理解ES6的迭代器與生成器
- ES6 迭代器與可迭代對象的實現(xiàn)
- 詳談ES6中的迭代器(Iterator)和生成器(Generator)
- ES6中的迭代器、Generator函數(shù)及Generator函數(shù)的異步操作方法
- ES6常用小技巧總結(jié)【去重、交換、合并、反轉(zhuǎn)、迭代、計算等】
相關(guān)文章
from表單多個按鈕提交用onclick跳轉(zhuǎn)不同action
這篇文章主要介紹了from表單多個按鈕提交用onclick跳轉(zhuǎn)不同action,需要的朋友可以參考下2014-04-04javascript ES6中箭頭函數(shù)注意細節(jié)小結(jié)
這篇文章主要給大家總結(jié)了關(guān)于javascript ES6中箭頭函數(shù)注意細節(jié)的相關(guān)資料,文中介紹的比較詳細,需要的朋友可以參考借鑒,下面來一起看看吧。2017-02-02如何利用Three.js實現(xiàn)web端顯示點云數(shù)據(jù)
這篇文章主要給大家介紹了關(guān)于如何利用Three.js實現(xiàn)web端顯示點云數(shù)據(jù)的相關(guān)資料,最近在項目中遇到需求,需要在web端顯示點云數(shù)據(jù),將我的實現(xiàn)步驟介紹在這里供大家參考,需要的朋友可以參考下2023-11-11微信小程序?qū)崿F(xiàn)即時通信聊天功能的實例代碼
這篇文章主要介紹了微信小程序?qū)崿F(xiàn)即時通信聊天功能的實例代碼,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2018-08-08Js setInterval與setTimeout(定時執(zhí)行與循環(huán)執(zhí)行)的代碼(可以傳入?yún)?shù))
最近在做項目時用到了定時執(zhí)行的js方法,setInterval與setTimeout時間長了不用有些生疏了,所以自己總結(jié)了一下,記下來,以便以后使用。2010-06-06