引言
遍歷對象是平常工作中很常見的一個操作,幾乎是日常操作,但是遍歷對象真的是一件很容易的事情麼,顯然不是的。
常用的方式
for...in
for (variable in object) {...}
這個是一個很常見的用法,相信每個人順手都可以寫出來。但是這裏需要主要的是一段這個遍歷的定義
for...in語句以任意順序遍歷一個對象自有的、繼承的、可枚舉的、非Symbol的屬性。對於每個不同的屬性,語句都會被執行。
一個一個詞摳吧。
對象自有的
如果一個key是對象自有的那麼一定可以用obj.hasOwnProperty(prop)的返回值來判斷
繼承的
這個也很好理解,比如下面的例子
var parent = {a: 1};
function child() {
this.b = 'b';
}
child.prototype = parent;
var obj = new child();
此時由於obj的原型鏈繼承了parent,所以其實obj是有a屬性的。換句話說for in會遍歷對象原型鏈上的屬性
可枚舉的
什麼是可枚舉的詳細的可以看一下這個鏈接
首先對象的屬性分爲兩種,數據屬性如a['b']=1,這個就是數據屬性,另一種就是訪問器屬性,也就是我們用的getter。
這兩種屬性都有一個特性的[[Enumerable]],這個布爾值代表了這個屬性是否可以被枚舉。如果一個對象的屬性被設定爲不可枚舉,那麼for in並不可以遍歷到。可枚舉性可以用propertyIsEnumerable來判斷。
非Symbol
Symbol是什麼這裏不展開說了不熟悉的建議看一下es6 symbol
symbol可以被用作給某個對象做私有屬性,而如果屬性值是symbol類型的那麼for in也是無法遍歷的。
小結
綜上可以明確的知道到底對象的哪些屬性可以用for in去遍歷出來了,坑點在基礎和可枚舉。數組遍歷我們會自然的去用for in,但是大家是否考慮過,數組也是一個對象,爲什麼我們再用for in的時候不會把數組的長度,length作爲一個屬性遍歷到呢,原因也就是length屬性其實是一個不可枚舉屬性
Object.keys()
Object.keys() 方法會返回一個由一個給定對象的自身可枚舉屬性組成的數組,數組中屬性名的排列順序和使用 for...in 循環遍歷該對象時返回的順序一致。
注意點這裏和for in有一個很大的區別,就是這個返回的是對象自身可枚舉屬性組成的數組,不包含繼承
var parent = {a: 1};
function child() {
this.b = 'b';
}
child.prototype = parent;
var obj = new child();
Object.keys(obj); //['b']
Object.getOwnPropertyNames(obj)與Object.getOwnPropertySymbols(obj)
getOwnPropertyNames方法返回一個由指定對象的所有自身屬性的屬性名(包括不可枚舉屬性但不包括Symbol值作爲名稱的屬性)組成的數組。
getOwnPropertySymbols方法返回一個給定對象自身的所有 Symbol 屬性的數組。
關鍵詞,所有的,自身的。這兩個方法不受是否可枚舉屬性的限制,而且是隻返回自身的,所以Object.getOwnPropertyNames的返回值一定是包含了Object.keys的返回值
Reflect.ownKeys(obj)
Reflect.ownKeys 方法返回一個由目標對象自身的屬性鍵組成的數組。它的返回值等同於Object.getOwnPropertyNames(target).concat(Object.getOwnPropertySymbols(target))。
for...of
接下來說說另一種遍歷的方案。for of 與for in 不同的就是for of是在可迭代對象(包括 Array,Map,Set,String,TypedArray,arguments 對象等等)上創建一個迭代循環,調用自定義迭代鉤子,併爲每個不同屬性的值執行語句。下面還是進入摳關鍵詞的階段
可迭代對象
什麼是可迭代對象?遵循有可迭代協議的對象成爲可迭代對象,用人話說就是一個對象如果有[Symbol.iterator],那麼他就是可迭代對象。Symbol.iterator是es6提供了 1個內置的 Symbol 值。
iterator簡單說就是一個有一個next函數,這個函數執行的返回值一定是一個對象,對象有兩個屬性done標記迭代是否結束,value標記這次迭代的結果值。
如何用for...of遍歷對象
綜上所述也就是說給你的要遍歷的對象增加一個Symbol.iterator就可以了
拓展運算符
除了上面說的還想再補充一種遍歷的場景,對象的拓展運算符,那麼對象的拓展運算符究竟是有哪些屬性可以被賦值。
自身的,可枚舉的。可以看兩個例子
var obj = {}
Object.defineProperty(obj, 'key', {
enumerable: false,
configurable: true,
writable: true,
value: "a"
});
b = {...obj};
console.log(b); //{}
可以看到不可枚舉屬性在解構賦值中是不可被賦值的。
var parent = {a: 1};
function child() {
this.b = 'b';
}
child.prototype = parent;
var obj = new child();
var b = {...obj};
console.log(b);//{b: 'b'}
可以看到繼承的屬性在解構賦值中是不可被賦值的。
總結
對象的遍歷方法很多,但是要根據具體對象屬性的特徵和應用場景,還有兼容性來選擇最適合的遍歷方案。