Iterator
這真是毅種迴圈
Iterator不是array,也不是set,不是map, 它不是一個實體,而是一種訪問機制
,是一個用來訪問某個物件的介面規範,為各種不同的資料結構提供統一的訪問機制。任何資料結構只要部署Iterator介面,就可以完成遍歷操作
(即依次處理該資料結構的成員)。
Iterator的遍歷某個資料結構過程是這樣的(比如對array進行遍歷)
-
建立一個指標物件,指向當前陣列的起始位置
。也就是說,遍歷器物件本質上,就是一個指標物件 -
第一次呼叫指標物件的next方法
,可以將指標指向資料結構的第一個成員, 這時候的返回值是{value:array[0], done:false} -
第二次呼叫指標物件的next方法
,指標就指向資料結構的第二個成員,這時候的返回值是{value:array[1], done:false} -
不斷呼叫指標物件的next方法
,直到它指向資料結構的結束位置這時候的返回值是{value:array[array.length-1], done:true} -
遍歷完畢
為陣列[`a`,`b`]手動寫一個iterator介面
var itertaor = makeIterator([`a`, `b`]);
iterator.next() // { value: "a", done: false }
iterator.next() // { value: "b", done: false }
iterator.next() // { value: undefined, done: true }
function makeIterator(array) {
var nextIndex = 0;
return {
next: function() {
return nextIndex < array.length ?
{value: array[nextIndex++], done: false} :
{value: undefined, done: true};
}
};
}
function makeIterator(array) {
var nextIndex = 0;
return {
next: function() {
return nextIndex < array.length ?
{value: array[nextIndex++], done: false} :
{value: undefined, done: true};
}
};
}
等價於:
let iterator =
makeIterator([`a`,`b`])=
{next:function(){xxxx}}
等價於:
iterator.next()
= nextIndex<array.length
?{value: array[nextIndex++], done: false}
:{value: undefined, done: true};
-
所以當
var iterator = makeIterator([`a`,`b`])
執行完,這時候makeIterator函式內部的nextIndex=0
, 但由於makeIterator
給外部變數iterator
返回了一個引用{next:function(){xxx}}
,所以makeIterator函式無法關閉,現在處於記憶體洩漏狀態(函式的執行上下文環境沒有關閉啊我摔!), -
執行完第一個iterator.next()的時候, 由於此時nextIndex=0, nextIndex<array.length成立,返回的是
{value: array[nextIndex++], done: false}
,這一句實際等價於: 返回{value: array[nextIndex], done: false}
然後nextIndex=nextIndex+1
,現在nextIndex=1了 -
執行完第二個iterator.next()的時候, 由於此時nextIndex=1, nextIndex<array.length成立,返回的是
{value: array[nextIndex++], done: false}
,這一句實際等價於: 返回{value: array[nextIndex], done: false}
然後nextIndex=nextIndex+1
,現在nextIndex=2了 -
執行完第三個iterator.next()的時候, 由於此時nextIndex=2, nextIndex<array.length不成立,返回的是
{value: undefined, done: true}
可遍歷物件
事實上,我們不需要手動寫array/set/map
的Iterator介面了,因為ES6規定它們是可遍歷物件
,所以對應的Iterator介面已經被新增到對應的Symbol.iterator
屬性上了(所以這三種資料結構才能用keys(for..of)/values(for..of)/entries(for..of)/foreach
這種方法進行遍歷),你可以通過如下方式來獲取
let arr = [`a`, `b`, `c`];
let arrIterator = `arr[Symbol.iterator]()`
let set = new Set([1,2,3]);
let setIterator = `set[Symbol.iterator]()`
let map = new Map([x,1][y,2]);
let mapIterator = `map[Symbol.iterator]()`
非可遍歷物件
對於非可遍歷物件,比如{a:1,b:2},ES6並沒有規定這種物件鍵值對為可遍歷物件,所以用for..of是不能對它進行遍歷的.如果要使一個物件變成可遍歷的,需要手動新增Iterator規則介面到它的Symbol.iterator
屬性上去,比如
let obj = {
data: [ `hello`, `world` ],
[Symbol.iterator]() {
const self = this;
let index = 0;
return {
next() {
if (index < self.data.length) {
return {
value: self.data[index++],
done: false
};
} else {
return { value: undefined, done: true };
}
}
};
}
};
對於其他的不可遍歷物件(比如類陣列物件),你可以手動在Symbol.iterator
屬性上部署上手寫的Iterator介面(這樣你自己可以寫遍歷規則),也可以讓Symbol.iterator
屬性直接指向array的Iterator介面(這樣就是用的陣列的遍歷規則)