遍歷一個對象

引言

遍歷對象是平常工作中很常見的一個操作,幾乎是日常操作,但是遍歷對象真的是一件很容易的事情麼,顯然不是的。

常用的方式

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'}

可以看到繼承的屬性在解構賦值中是不可被賦值的。

總結

對象的遍歷方法很多,但是要根據具體對象屬性的特徵和應用場景,還有兼容性來選擇最適合的遍歷方案。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章