Iterator和for...of
Iterator和for...of
什麼是Iterator
ES6中的Map 和 Set ,再加上之前的陣列和物件,這樣就有了四種資料集合,使用者可以組合使用它們,定義自己的資料結構。這時,我們就需要一個統一的機制,來處理這幾種不同的和資料結構。
Iterator 遍歷器就是這樣一種機制,它是一種介面,為各種不同的資料結構提供統一的訪問機制。
其作用有下:
- 為各種資料結構提供統一簡便的訪問介面
- 使得資料結構成員能夠按照某種次序排列
- 為es6中新的遍歷方法 for...of 服務
Iterator的遍歷過程
-
建立一個指標物件,指向當前資料結構的起始位置。
遍歷器的本質,就是一個指標物件
-
第一次呼叫指標物件的
next
方法,可以將指標指向資料結構的第一個成員 -
第二次呼叫指標物件的
next
方法,指標就指向資料結構的第二個成員 -
如上不停的呼叫
next
方法,直到它指向資料結構的結束位置學到這裡突然想到了連結串列,哈哈哈 ~ ~
每一次呼叫 next
方法,都會返回資料結構的當前成員的資訊 。具體來說,就是返回一個包含 value
和 done
兩個屬性的物件。其中, value
屬性是當前成員的值, done
屬性是一個布林值,表示遍歷是否結束。
手寫一個 Iterator介面
const arr = [1, 2, 3] function iterator(arr) { let index = 0 return { next: function () { return index < arr.length ? { value: arr[index++], done: false } : { value: undefined, done: true } } } } const it = iterator(arr) console.log(it.next()) console.log(it.next()) console.log(it.next()) console.log(it.next())
執行結果如下圖:
Symbol.iterator屬性
凡是具有Symbol.iterator屬性的資料結構都自帶具有Iterator 介面
原生具備 Iterator 介面的資料結構如下:
- Array
- Map
- Set
- String
- TypedArray
- 函式的 arguments 物件
- NodeList 物件
拿陣列為例:
let arr = ['a', 'b', 'c']; let iter = arr[Symbol.iterator](); iter.next() // { value: 'a', done: false } iter.next() // { value: 'b', done: false } iter.next() // { value: 'c', done: false } iter.next() // { value: undefined, done: true }
給物件新增Symbol.iterator屬性
另外一些資料結構沒有(比如物件):
使用for of作用於物件時:
// Uncaught TypeError: obj is not iterable
const obj = {} console.log(obj[Symbol.iterator]) // undefined
凡是具有Symbol.iterator屬性的資料結構都可以被for...of 迴圈呼叫,我們可以手動的給物件新增 Symbol.iterator
屬性
一個物件如果要具備可被 for...of
迴圈呼叫的 Iterator 介面,就必須在 Symbol.iterator
的屬性上部署遍歷器生成方法(原型鏈上的物件具有該方法也可)。
下面是通過遍歷器實現指標結構的例子:
function Obj(value) { this.value = value this.next = null } Obj.prototype[Symbol.iterator] = function() { var iterator = {next: next} var current = this function next() { if (current) { var value = current.value current = current.next return { value: value, done: false } } else { return {done: true} } } return iterator } var one = new Obj(1); var two = new Obj(2); var three = new Obj(3); one.next = two; two.next = three; for (var i of one) { console.log(i); // 1, 2, 3 }
簡單一點,我們可以這樣實現:
var obj = { a: 1, b: 2, 'me': 'akong', 'lover': 'xwr' } obj[Symbol.iterator] = function () { let keys = Object.keys(obj) let len = keys.length let n = 0 return { next: function() { if (n < len) { return { value: keys[n++], done: false } } else { return { done: true } } } } } for (var attr of obj) { console.log(attr) }
這樣我們就可以遍歷obj物件啦~!
呼叫 Iterator 介面的場合
(1)解構賦值
對陣列和 Set 結構進行解構賦值時,會預設呼叫 Symbol.iterator
方法。
let set = new Set().add('a').add('b').add('c'); let [x,y] = set; // x='a'; y='b' let [first, ...rest] = set; // first='a'; rest=['b','c'];
(2)擴充套件運算子
擴充套件運算子(...)也會呼叫預設的 Iterator 介面。
// 例一 var str = 'hello'; [...str] //['h','e','l','l','o'] // 例二 let arr = ['b', 'c']; ['a', ...arr, 'd'] // ['a', 'b', 'c', 'd']
(3)其他場合
由於陣列的遍歷會呼叫遍歷器介面,所以任何接受陣列作為引數的場合,其實都呼叫了遍歷器介面。下面是一些例子。
new Map([['a',1],['b',2]])