在JavaScript中,對象的屬性分爲可枚舉和不可枚舉之分,它們是由屬性的enumerable值決定的,我們應該清楚什麼是枚舉屬性,什麼是不可枚舉屬性?
1.怎麼判斷屬性是否可枚舉
一.propertyIsEnumerable方法
每個對象都有一個 propertyIsEnumerable 方法。此方法可以確定對象中指定的屬性是否可以被枚舉,但是通過原型鏈繼承的屬性除外。如果對象沒有指定的屬性,則此方法返回 false,propertyIsEnumerable() 方法返回一個布爾值,表示指定的屬性是否可枚舉。
var arr = [1,2,3,4];
Array.prototype.method = function(){
console.log("method");
};
console.log(arr.propertyIsEnumerable(0));//true arr[0]===1
console.log(Array.prototype.propertyIsEnumerable("method"));//true
console.log(Array.prototype.propertyIsEnumerable("concat"));//flase
二.Object.getOwnPropertyDescriptor方法
返回指定對象上一個自有屬性對應的屬性描述符。(自有屬性指的是直接賦予該對象的屬性,不需要從原型鏈上進行查找的屬性)
var arr = [1,2,3,4];
Array.prototype.method = function(){
console.log("method");
};
console.log(Object.getOwnPropertyDescriptor(arr,0)); //{value: 1, writable: true, enumerable: true, configurable: true}
console.log(Object.getOwnPropertyDescriptor(Array.prototype,"method"));//{value: ƒ, writable: true, enumerable: true, configurable: true}
console.log(Object.getOwnPropertyDescriptor(Array.prototype,"split"));//{value: ƒ, writable: true, enumerable: true, configurable: true}
console.log(Object.getOwnPropertyDescriptor(Array.prototype,"concat"));//{value: ƒ, writable: true, enumerable: false, configurable: true}
主要是enumerable這個屬性來決定是否可枚舉!
1.value:代表該屬性的值
2.writable :當且僅當屬性的值可以被改變時爲true。(僅針對數據屬性描述有效)
3.enumerable:當且僅當指定對象的屬性可以被枚舉出時,爲 true
4.configurable:當且僅當指定對象的屬性描述可以被改變或者屬性可被刪除時,爲true。
2.修改是否可枚舉
Object.defineProperty方法(直接在一個對象上定義一個新屬性,或者修改一個對象的現有屬性,並返回此對象)
var arr = [1,2,3,4];
Array.prototype.method = function(){
console.log("method");
};
Object.defineProperty(Array.prototype,"method",{
enumerable:false
})
console.log(Array.prototype.propertyIsEnumerable("method"));//false
3.對比循環
有了上面的瞭解,我們再來說說 for in 和for循環的吧
一.for…in 循環只遍歷可枚舉屬性(包括它的原型鏈上的可枚舉屬性)
它會把原型鏈上的屬性遍歷出來,不過你可以通過hasOwnProperty過濾掉!for循環不會遍歷出原型鏈上的屬性!
var arr = [1,2,3,4];
Array.prototype.method = function(){
console.log("method");
};
for(var k in arr){
console.log(arr[k]);//1,2,3,4,ƒ (){console.log("method");}
};
for(var k in arr){
if(arr.hasOwnProperty(k)){
console.log(arr[k]);//1,2,3,4
};
};
//看下for循環
for(var i =0 ;i<arr.length;i++){
console.log(arr[i]);//1,2,3,4
}
二.for…in和for遍歷數組時下標類型不一樣
var arr = [1,2,3,4];
for(var k in arr){
if(arr.hasOwnProperty(k)){
console.log(typeof k);//string
};
};
for(var i =0 ;i<arr.length;i++){
console.log(typeof i); //number
}
for in 的下標是string,for下標是number,這點比較容易忽略!
三.對稀疏數組的執行效率
什麼是稀疏呢?稀疏也就是說,數組中的元素之間可以有空隙!
var arr = [1,2,3,4];
arr.length =10;
console.log(arr); //這個就是稀疏數組 [1, 2, 3, 4, empty × 6]
再來看下執行效率
var arr = [1, 2, 3, 4];
arr.length = 10;
console.log(arr);
for (var k in arr) {
if (arr.hasOwnProperty(k)) {
console.log(k); //0,1,2,3
};
};
for (var i = 0; i < arr.length; i++) {
console.log(i); //0,1,2,3,4,5,6,7,8,9
}
我們知道如果將其length屬性設置爲大於數組項數的值,其實後面多出的每一項都會是undefined值,for in 只執行4次,而for循環執行了10次,
console.log(arr.propertyIsEnumerable(3));//true
console.log(arr.propertyIsEnumerable(4));//false
console.log(arr.propertyIsEnumerable(5));//false
console.log(arr.propertyIsEnumerable(6));//false
console.log(arr.propertyIsEnumerable(7));//false
console.log(arr.propertyIsEnumerable(8));//false
console.log(arr.propertyIsEnumerable(9));//false
console.log(arr.propertyIsEnumerable(10));//false
這個就是爲什麼for in 只執行4次,因爲for in 對於不存在的項是不會被枚舉出來的!