對象屬性的可枚舉性
JavaScript中對象的屬性分為兩種:數據屬性和訪問器屬性。然後根據具體的上下文環境的不同,又可以將屬性分為:原型屬性和實例屬性。原型屬性是定義在對象的原型(prototype
)中的屬性,而實例屬性一方面來自構造的函數中,然後就是構造函數實例化後添加的新屬性。
在JavaScript中除了檢測對象的屬性是否存在,還會經常對對象的屬性進行遍歷(枚舉)。而在JavaScript中遍歷一個對象的屬性並不太簡單,主要有兩個原因:
- JavaScript中的對象通常都處在某個原型鏈中,它會從一個或多個的上層原型上繼承一些屬性
- JavaScript中的屬性不光有值,它還有一些除了值以外的其他特性,其中一個影響屬性遍歷的特性就是
[[Enumerable]]
true
,則這個屬性是可枚舉的,否則反之
這篇文章將總結有關於JavaScript中對象屬性枚舉的幾種方法:
for ... in
Object.keys()
Object.getOwnPropertyNames()
for ... of
for ... in
for...in
循環可以遍歷對象中所有可枚舉的對象屬性(包括對象自有屬性和繼承的屬性)。不過需要註意的是,使用for...in
循環遍歷對象屬性時返回的屬性會因為各個瀏覽器不同導致對象屬性遍歷的順序有可能不是當初構建時的順序。
var obj = {
‘x‘: 1,
‘y‘: 2,
‘z‘: 3
}
obj.propertyIsEnumerable(‘toString‘); // false,不可枚舉
for (prop in obj) {
console.log(prop); // 輸出x,y,z;但不會輸出toString
}
其實for...in
操作的主要目的就是遍歷對象的屬性,如果只需要獲取對象的實例屬性(跳過繼承屬性),可以使用hasOwnProperty()
進行過濾:
for (prop in obj) {
if (!obj.hasOwnProperty(prop)) continue; // 跳過繼承屬性
}
如此一來,可以這樣來使用for...in
循環遍歷對象屬性:
(function () {
var getEnumPropertyNames = function (obj) {
if (typeof obj !== ‘object‘) throw TypeError(); // 參數必須是對象
var props = []; // 將要返回的數組
for (var prop in obj) { // 遍歷所有可枚舉的屬性
if (obj.hasOwnProperty(prop)) { //判斷是否是自有屬性
props.push(prop); //將屬性名添加到數組中
}
}
return props; //返回這個數組
}
// 實例化
var obj = {
‘x‘: 1,
‘y‘:2
}
obj.propertyIsEnumerable(‘toString‘)
var propertys = getEnumPropertyNames(obj);
console.log(propertys.length); //2
console.log(propertys.join(",")); //x,y
})()
Object.keys()
Object.keys()
方法會返回一個由給定對象的所有可枚舉自身屬性的屬性名組成的數組,數組中屬性名的排列順序和使用for...in
循環遍歷該對象時返回的順序一致。兩者最大的區別在於for...in
還會遍歷出一個對象從其原型鏈上繼承到的可枚舉屬性。
Object.keys()
返回一個所有元素為字符串的數組,其元素來自於從給定的對象上面可直接枚舉的屬性。這些屬性的順序與手動遍歷該對象屬性時的一致。
var obj = {
‘x‘: 1,
‘y‘: 2,
‘z‘: 3
}
obj.propertyIsEnumerable(‘toString‘);
console.log(Object.keys(obj)); // ["x", "y", "z"]
Object.keys()
可以遍歷對象可枚舉的自身屬性。也就是說對象的屬性不是從原型鏈上繼承下來的。
註意:在 ES5 環境,如果傳入的參數不是一個對象,而是一個字符串,那麽它會報 TypeError。在 ES6 環境,如果傳入的是一個非對象參數,內部會對參數作一次強制對象轉換,如果轉換不成功會拋出 TypeError。
// 在 ES5 環境
Object.keys(‘foo‘); // TypeError: "foo" is not an object
// 在 ES6 環境
Object.keys(‘foo‘); // ["0", "1", "2"]
// 傳入 null 對象
Object.keys(null); // Uncaught TypeError: Cannot convert undefined or null to object
// 傳入 undefined
Object.keys(undefined); // Uncaught TypeError: Cannot convert undefined or null to object
Object.getOwnPropertyNames()
Object.getOwnPropertyNames()
方法返回一個由指定對象的所有自身屬性的屬性名(包括不可枚舉屬性)組成的數組,但不會獲取原型鏈上的屬性。該數組對元素是 obj
自身擁有的枚舉或不可枚舉屬性名稱字符串。 數組中枚舉屬性的順序與通過for...in
(或 Object.keys()
)叠代該對象屬性時一致。 數組中不可枚舉屬性的順序未定義。
// 類數組對象
var obj = {
0: "a",
1: "b",
2: "c"
};
console.log(Object.getOwnPropertyNames(obj).sort()); // ["0", "1", "2"]
for...of
for...of
為ES6新增的方法,主要來遍歷可叠代的對象(包括Array
, Map
, Set
, arguments
等),它主要用來獲取對象的屬性值,而for...in
主要獲取對象的屬性名。
var colors = [‘red‘, ‘green‘, ‘blue‘];
colors.length = 5;
colors.push(‘yellow‘);
for (var i in colors) {
console.log(colors[i]); // red green blue yellow
}
for (var j of colors) {
console.log(j); // red green blue undefined undefined yellow
}
可以看到使用for...of
可以輸出包括數組中不存在的值在內的所有值。
其實除了使用for...of
直接獲取屬性值外,我們也可以利用Array.prototype.forEach()
來達到同樣的目的。
var colors = [‘red‘, ‘green‘, ‘blue‘];
colors.foo = ‘hello‘;
Object.keys(colors).forEach(function(elem, index) {
console.log(colors[elem]); // red green blue hello
console.log(colors[index]); // red green blue undefined
});
colors.forEach(function(elem, index) {
console.log(elem); // red green blue
console.log(index); // 0 1 2
})
總結
這幾個方法之間的差異主要在屬性是否可可枚舉,是來自己原型,還是實例。著作權歸作者所有。
原文: https://www.w3cplus.com/javascript/how-do-i-enumerate-the-properties-of-a-javascript-object.html?utm_source=tuicool&utm_medium=referral ? w3cplus.com
對象屬性的可枚舉性