JavaScript中Iterator迭代器接口和循環
JavaScript的迭代器(Iterator)介紹
迭代器是數據結構遍歷的一種機制(或者是什麼我也不太懂的行業術語),為數據結構定義瞭統一的遍歷規則。 迭代器(Iterator)主要是提供for...of
使用,或者說,數據結構隻有實現瞭迭代器(Iterator)接口才能使用for...of
進行遍歷數據,當一種數據結構沒有實現迭代器(Iterator)接口時,去使用for...of
操作,就會被拋出異常Uncaught TypeError: xxx is not iterable
。
JavaScript的迭代器(Iterator)的接口規范和操作過程:
- 迭代器被調用時,返回一個指針對象,指針對象中必須包含一個
next()
的方法,每次調用next()
方法,都會返回一個代表當前成員的信息對象,具有value
和done
兩個屬性。value
可為任意數據類型,done
則是一個佈爾類型,當調用next
的方法時返回的對象中的done
屬性為false
時,表示還可以繼續進行遍歷,當done
屬性為true
時,表示遍歷結束(沒有的東西遍歷瞭)瞭。 - 迭代器(Iterator)對象的可選屬性
return()
方法和throw()
方法,也就是說當我們要自己去實現迭代器(Iterator)的時候,迭代器裡必須要有next()
方法,而return()
方法和throw()
是否要實現是可選的。而自己實現迭代器(Iterator)時,不管是next()
、return()
還是throw()
方法必須有返回值並且是對象,否則進行遍歷的時候會拋出異常Uncaught TypeError: Iterator result xxx is not an object
。 - 自己實現迭代器,隻要給數據結構或者對象添加
[Symbol.iterator]
屬性即可,其值必須是一個函數,返回一個隻針對象,需遵循第2點的規范。為什麼必須是[Symbol.iterator]
的屬性名,因為JavaScript的定義中,尋找遍歷器時就是用這個字段,這也是一個標準規范。
下面是自己實現迭代器的演示代碼:
const obj = {a: "age 18", b: 2}; // 實現迭代器 obj[Symbol.iterator] = function () { const keys = Object.keys(obj); let keyIndex = 0; return { next() { if (keys.length === 0 || keyIndex >= keys.length) { return { value: undefined, done: true } } const key = keys[keyIndex], value = [key, obj[key]]; keyIndex += 1; return { value, done: false } } }; } // 使用for...of進行遍歷 for (let [key, value] of obj) { console.log(`${key}--${value}`) } // a--age 18 // b--2
返回參數簡寫,當返回正常值的時候,done
字段可以省略,當循環結束的時候,value
可以省略。但是註意不能兩個都不寫,不然會死循環,而且必須要有條件結束的操作,不然也會死循環,就像遞歸一樣,一定要有條件結束的操作
Number.prototype[Symbol.iterator] = function () { let that = +this, i = that < 0 ? that : 0; that = that < 0 ? 0 : that; return { next() { if (i <= that) { const value = i; i += 1; return { value }; } return { done: true }; } }; }; for (const item of 20) { console.log(item); } // 數組的擴展運算符也是調用迭代器的哦 console.log([...5]);// [0, 1, 2, 3, 4, 5]
迭代器的可選參數return()
和throw()
return()
方法是在遍歷中斷的時候會調用,如使用瞭break
關鍵字中斷或者拋出瞭異常都會調用這個方法。return()
方法必須有返回參數並且要求是object
類型的數據,否則就會拋出異常Uncaught TypeError: Iterator result undefined is not an object
,至於object
裡內容要求是什麼,我測瞭一下,毫無影響,返回空對象也沒問題。
const obj = { size: 5, [Symbol.iterator]() { return { // 這裡用箭頭函數為的是可以直接使用this next: () => { if (this.size >= 0) { const value = this.size; this.size -= 1; return { value }; } return { done: true }; }, return() { console.log("中斷瞭"); return { done: true }; // 返回以下內容照樣不會有問題,但是最好不這麼操作,因為代碼具有語義化才能更好的閱讀 // return {}; // return new Date(); } }; } }; for (const item of obj) { if (item < 3) { break;// 中斷瞭 } console.log(item); }
throw()
方法在迭代器中基本用不到,而是配合Generator
使用,這裡就不做過多的敘述。
原生具備 Iterator 接口的數據結構如下:
- Array
- Map
- Set
- String
- TypedArray
- 函數的 arguments 對象
- NodeList 對象
for…of循環與for…in循環
上面已經詳細的說明瞭迭代器(Iterator)主要是提供for...of
循環使用,所以for...of
循環時調用的是迭代器(Iterator),而循環的值是由實現的迭代器(Iterator)而定,而for...in
循環是循環鍵值。
const arr = ["a", "b", "c"]; // 原生的數組迭代器(Iterator)的實現遍歷時返回的是每一個元素 for (const item of arr) { console.log(item); // a // b // c } // for...in 返回的是key,這裡是數組,key就是索引 for (const key in arr) { console.log(key); // 0 // 1 // 2 }
還有一個更直觀的區別。for...of
隻是根據迭代器(Iterator)實現的內容返回結果,所以就不會遍歷不在范圍的東西,而for...in
會把所有的鍵遍歷出來。
const arr = ["a", "b", "c"]; arr.testValue = 1; for (const item of arr) { console.log(item); // a // b // c } for (const item in arr) { console.log(item); // 0 // 1 // 2 // testValue }
到此這篇關於JavaScript中Iterator迭代器接口與循環的文章就介紹到這瞭,更多相關JavaScript Iterator 內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- JavaScript前端迭代器Iterator與生成器Generator講解
- ES6的循環與可迭代對象示例詳解
- 簡單談談JavaScript變量提升
- JavaScript循環遍歷的24個方法,你都知道嗎
- 詳解JS ES6變量的解構賦值