遍歷器(Iterator)是一種接口,爲各類不一樣的數據結構提供統一的訪問機制。任何數據結構只要部署 Iterator 接口,就能夠完成遍歷操做(即依次處理該數據結構的全部成員)。數組
其實iterator在之前是內置在JavaScript中,主要是Array和Object表示的集合數據結構使用。ES6新增了map和set數據結構,這樣就有了四種數據集合。這樣就須要一個統一接口機制來處理不一樣的數據結構。Iterator就是這個做用。bash
Iterator 的做用有三個:數據結構
爲各類數據結構,提供一個統一的、簡便的訪問接口;函數
使得數據結構的成員可以按某種次序排列;ui
ES6 創造了一種新的遍歷命令for...of循環,Iterator 接口主要供for...of消費。spa
Interator內部遍歷過程:prototype
建立一個指針對象,指向當前數據結構的起始位置。遍歷器對象本質上,就是一個指針對象。指針
第一次調用指針對象的next方法,能夠將指針指向數據結構的第一個成員。code
第二次調用指針對象的next方法,指針就指向數據結構的第二個成員。orm
不斷調用指針對象的next方法,直到它指向數據結構的結束位置。
每一次調用next方法,都會返回數據結構的當前成員的信息。就是返回一個包含value和done兩個屬性的對象。value屬性是當前成員的值,done屬性是一個布爾值,表示遍歷是否結束。
咱們說了,Iterator 接口的目的,就是爲全部數據結構,提供了一種統一的訪問機制,主要提供給ES6的for of使用。當咱們使用for of的時候會自動尋找Interator接口。一種數據結構只要部署了Iterator接口,咱們就稱之爲可遍歷的(iterable)。
ES6 規定,默認的 Iterator 接口部署在數據結構的Symbol.iterator屬性,或者說,一個數據結構只要具備Symbol.iterator屬性,就能夠認爲是「可遍歷的」(iterable)。
Symbol.iterator屬性自己是一個函數,就是當前數據結構默認的遍歷器生成函數。執行這個函數,就會返回一個遍歷器。屬性名Symbol.iterator,它是一個表達式,返回Symbol對象的iterator屬性,這是一個預約義好的、類型爲 Symbol 的特殊值,因此要放在方括號內。
ES6 的有些數據結構原生具有 Iterator 接口(好比數組),即不用任何處理,就能夠被for...of循環遍歷。緣由在於,這些數據結構原生部署了Symbol.iterator屬性,另一些數據結構沒有(好比對象)。凡是部署了Symbol.iterator屬性的數據結構,就稱爲部署了遍歷器接口。調用這個接口,就會返回一個遍歷器對象。
咱們給一個obj部署:
const obj = {
[Symbol.iterator] : function () {
return {
next: function () {
return {
value: 1,
done: true
};
}
};
},
a: 1};
console.log(obj[Symbol.iterator]().next());//{value: 1, done: true}
複製代碼
原生具有 Iterator 接口的數據結構以下。
Array
Map
Set
String
TypedArray
函數的 arguments 對象
NodeList 對象
咱們使用數組的Symbol.iterator屬性:
var arr = [1, 2];
var arrIter = arr[Symbol.iterator]();
console.log(arrIter.next());//{value: 1, done: false}
console.log(arrIter.next());//{value: 2, done: false}
console.log(arrIter.next());//{value: undefined, done: true}
複製代碼
對於原生部署 Iterator 接口的數據結構,不用本身寫遍歷器生成函數,for...of循環會自動遍歷它們。除此以外,其餘數據結構(主要是對象)的 Iterator 接口,都須要本身在Symbol.iterator屬性上面部署,這樣纔會被for...of循環遍歷。
要注意的是,部署了Symbol.iterator屬性的並不能直接用for of循環,想要使用for of循環還須要在Symbol.iterator屬性上部署遍歷器生成方法。這邊分享相似數組的對象調用數組的Symbol.iterator例子,其餘的能夠自行研究:
var iterable = {
0: 'a',
1: 'b',
2: 'c',
length: 3,
[Symbol.iterator]: Array.prototype[Symbol.iterator]
};
for (var item of iterable) {
console.log(item); // a b c
}
複製代碼
普通對象這樣使用是沒有效果的。
雖然遍歷器接口Iterator(就是Symbol.iterator)是給for of使用,可是其餘不少會默認調用Symbol.iterator方法:
解構賦值、擴展運算符、yield*、Array.form、Map()、Set()、WeakSet()、WeakMap()、Promise.all()、Promise.race()等。
字符串是一個類數組,原生具備Iterator接口:
var str = 'abc';
var strIter = str[Symbol.iterator]();
console.log(strIter.next());//{value: "a", done: false}
console.log(strIter.next());//{value: "b", done: false}
console.log(strIter.next());//{value: "c", done: false}
console.log(strIter.next());//{value: undefined, done: true}
複製代碼
遍歷器對象除了具備next方法,還能夠具備return方法和throw方法。若是你本身寫遍歷器對象生成函數,那麼next方法是必須部署的,return方法和throw方法是否部署是可選的。
return方法的使用場合是,若是for...of循環提早退出(一般是由於出錯,或者有break語句),就會調用return方法。若是一個對象在完成遍歷前,須要清理或釋放資源,就能夠部署return方法。throw方法主要是配合 Generator 函數使用
一個數據結構只要部署了Symbol.iterator屬性,就被視爲具備 iterator 接口,就能夠用for...of循環遍歷它的成員。也就是說,for...of循環內部調用的是數據結構的Symbol.iterator方法。
for...of循環可使用的範圍包括數組、Set 和 Map 結構、某些相似數組的對象(好比arguments對象、DOM NodeList 對象)、後文的 Generator 對象,以及字符串。
var str = 'abc';
for(var val of str){
console.log(val);
}
複製代碼
有了for of,不少遍歷的操做均可以使用,配合entries、keys、values,好比對象不能直接用for of遍歷,咱們能夠用keys方法把對象的鍵變成數組去遍歷。
相對於其餘遍歷方法,for of會有一些優點,最大的優點我以爲就是提供了遍歷全部數據的統一操做接口。