for in
for in可以用來遍歷對象的可枚舉屬性列表(包括[[prototype]]鏈),單獨使用in操作符會檢查屬性是否在對象及其[[prototype]]原型鏈中(無論是否可以枚舉)
var obj = {
a: 2
}
console.log("a" in obj);//true
但是使用for in循環輸出的屬性名的順序是不可預測的,返回的先后次序因瀏覽器而異。
使用for in也可以遍歷數組,屬性為數組的索引,但是會有以下問題:
- index索引為字符串型數字,不能直接進行幾何運算
- 遍歷順序有可能不是按照實際數組的內部順序
- 使用for in會遍歷數組所有的可枚舉屬性,包括原型。例如例子中的原型方法method和name屬性
因此for in更適合用來遍歷對象
for of
前面說了for in遍歷的是key(鍵名),使用ES6的for of可以遍歷數組的value(鍵值),mdn上對于for of的說明
for...of語句在可迭代對象(包括 Array, Map, Set, String, TypedArray,arguments 對象等等)上創建一個迭代循環,對每個不同屬性的屬性值,調用一個自定義的有執行語句的迭代掛鉤
var arr = [ 1, 2, 3];
for (var v of arr) {
console.log( v );
}
// 1
// 2
// 3
for of循環不僅支持數組,實際上述定義的可迭代對象,可迭代對象指的是具備Iterator接口的數據結構
Iterator
實際上for of遍歷就是基于Iterator,當使用for of循環遍歷某種數據結構時,該循環會自動去尋找Iterator接口
遍歷器(Iterator)是一種接口,為各種不同的數據結構提供統一的訪問機制。任何數據結構只要部署Iterator接口,就可以完成遍歷操作(即依次處理該數據結構的所有成員)。
Iterator 的作用有三個:一是為各種數據結構,提供一個統一的、簡便的訪問接口;二是使得數據結構的成員能夠按某種次序排列;三是ES6創造了一種新的遍歷命令for...of循環,Iterator接口主要供for...of消費。
ES6 規定,默認的 Iterator 接口部署在數據結構的Symbol.iterator屬性,或者說,一個數據結構只要具有Symbol.iterator屬性,就可以認為是“可遍歷的”(iterable)。Symbol.iterator屬性本身是一個函數,就是當前數據結構默認的遍歷器生成函數。執行這個函數,就會返回一個遍歷器。至于屬性名Symbol.iterator,它是一個表達式,返回Symbol對象的iterator屬性
原生具備Iterator接口的數據結構如下
- Array
- Map
- Set
- String
- TypedArray
- 函數的arguments對象
- NodeList對象
for of循環首先會向北訪問對象請求一個迭代器對象,然后通過調用迭代器對象的next()方法來遍歷所有返回值
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 }
上面代碼中,變量arr是一個數組,原生就具有遍歷器接口,部署在arr的Symbol.iterator屬性上面。所以,調用這個屬性,就得到遍歷器對象。value是當前的遍歷值,done是一個布爾值,表示是否還有可以遍歷的值,這里遍歷到第三個時,已經沒有可以遍歷的值了,但是done仍然是false,下一個才是true,這個機制和ES6中發生器函數的語義相關,這里不做討論。
普通的對象沒有內置的Iterator接口,無法用for of遍歷,但是我們可以用Symbol.iterator來自己完成對對象的遍歷
var myObject = {
a: 2,
b: 3,
c: 4
};
Object.defineProperty( myObject, Symbol.iterator, {
enumerable: false,
writeable: false,
configurable: true,
value: function() {
var o = this;
var idx = 0;
var ks = Object.keys( o );
return {
next: function() {
return {
value: o[ks[idx++]],
done: (idx > ks.length)
};
}
};
}
});
var it = myObject[Symbol.iterator]();
it.next(); //{ value: 2, done:false}
it.next(); //{ value: 3, done:false}
it.next(); //{ value: 4, done:false}
it.next(); //{ value: undefined, done:true}
for (var v of myObject) {
console.log( v );
}
// 2
// 3
// 4