1. 程式人生 > 實用技巧 >JS中的可列舉屬性與不可列舉屬性

JS中的可列舉屬性與不可列舉屬性

什麼是列舉?列舉是指物件中的屬性是否可以遍歷出來,再簡單點說就是屬性是否可以以列舉出來。

一、怎麼判斷屬性是否可列舉

在JavaScript中,物件的屬性分為可列舉和不可列舉之分,它們是由屬性的enumerable值決定的。可列舉性決定了這個屬效能否被for…in查詢遍歷到。

js中的基本包裝型別的原型屬性是不可列舉的,比如Object,Array,Number等

這是一個例子:

var num = new Number();
for(var pro in num) {
   console.log("num." + pro + " = " + num[pro]);
}
//此處沒有輸出

它的輸出結果是空的,因為Number中的內建屬性是不可列舉的,所以不能被for ... in訪問到

每個物件都有propertyIsEnumerable()方法,這個方法可以判斷出指定的屬性是否可列舉。

用法:
obj.propertyIsEnumerable("屬性名");

function Person(){
    this.name = "我是例項屬性"
    this.age = 19;  
}
var p = new Person();
console.log(p.propertyIsEnumerable("name")); //true
 
Person.prototype.prop 
= "我是原型屬性" //新增一個原型屬性 console.log(p.propertyIsEnumerable("prop")); //false prop是繼承自原型上的屬性,所以返回的是false for(var k in p){ console.log(k+","+p[k]);//name,我是例項屬性 age,19 prop,我是原型屬性 }

從中也可以發現,如果是物件的原型鏈中的屬性,不管是否列舉都會返回false。
但是 for ...in 仍然可以讀出原型鏈中的可列舉屬性

二、列舉屬性的作用

列舉屬性主要會影響幾個方法

ES5中:
for...in //只遍歷物件自身的和繼承的可列舉的屬性


Object.keys() //返回物件自身的所有可列舉的屬性的鍵名
JSON.stringify //JSON.stringify() 方法用於將 JavaScript 值轉換為 JSON 字串。
ES6中:
Object.assign() //會忽略enumerable為false的屬性,只拷貝物件自身的可列舉的屬性。

可以看出來這些都是可以遍歷物件的方法,而這四個操作中只有for...in中會返回繼承的屬性

先看一個例子,建立一個"xsy"物件:

function Person(){
    this.name = "XSY"
};
Person.prototype = {
    constructor: Person,
    job:"student",
};
 
var xsy = new Person();
Object.defineProperty(xsy, "sex",{
    value:"female",
    enumerable:false
});

這裡用defineProperty方法定義了一個叫"sex"的不可列舉屬性

然後可以開始驗證了:

a. for...in

for(var pro in xsy){
    console.log("xsy." + pro+ " = " + xsy[pro]);
}

輸出的結果如下,可以發現 物件中宣告的屬性,原型鏈上繫結的屬性成功輸出了,而不可列舉屬性“sex”沒有輸出。

b. Object.keys()

console.log(Object.keys(xsy));

從輸出結果可以發現,這裡只輸出了物件宣告的可列舉屬性,但是沒有輸出原型鏈中的可列舉屬性

c.JSON.stringify

console.log(JSON.stringify(xsy));

這裡的輸出也和上面一樣,結果中只有物件中的可列舉屬性沒有原型鏈中的。

從上面這些操作中大概可以明白了,可列舉性決定了這個屬效能否被for…in查詢遍歷到。所以可列舉與否都是開發者自己定義的,
可以通過Object.defineProperty()方法。

三、設定可列舉屬性

其實在上面的例子中已經使用到了設定enumerable的方法:Object.defineProperty()

var person = {
    name:'xiao',
    age: '18',
    sex: 'boy'
}
 
Object.defineProperty(person,'age',{
    enumerable:true,//可以被列舉
});
Object.defineProperty(person,'sex',{
    enumerable:false,//不可以被列舉
})
 
for(var k in person){
    console.log(person[k])//a,可以被列舉
}
//18
//xiao

從上面可以看出:
1.Object.defineProperty(obj, prop, descriptor)方法有三那個引數
第一個:目標物件
第二個:目標屬性,字串
第三個:對目標屬性的行為,放在物件裡
2.enumerable為true時表示可列舉,enumerable為false表示不可列舉;
3.開發者自己定義的物件person中的所有屬性預設都是可列舉的;

四、如何判斷是否可列舉-- propertyIsEnumerable

有時候不知道物件的可列舉性,該怎麼判斷呢。propertylsEnumerable()方法可以解決這個問題

person.propertyIsEnumerable('sex');//false
person.propertyIsEnumerable('age');//true

propertyIsEnumerable() 語法:

  1. 語法:obj.propertyIsEnumerable(prop)
  2. 描述:每個物件都有一個propertyIsEnumerable方法。此方法可以確定物件中指定的屬性是否可列舉,返回一個布林值。但該方法對通過原型鏈繼承的屬性無效(原型鏈繼承的屬性是否可列舉不能用該方法來判斷)
  3. 案例:
  4. 1)使用者自定義物件和引擎內建物件的區別
Math.propertyIsEnumerable('random');   // 返回 false
Object.propertyIsEnumerable('constructor');    // 返回 false
var num = new Number();
for(var pro in num) {
    console.log("num." + pro + " = " + num[pro]);
}//輸出空
  1. 這說明了開發者自定義的屬性在一般情況下時可以列舉的,但是內建的物件Math和基本包裝型別裡的屬性是不可列舉的,如Object, Array, Number等;其實,propertyIsEnumerable方法只對物件自身的屬性(物件自身新增的、建構函式例項化的)有效,對原型上的、繼承來的屬性都無效。

五、總結

  1. for...in迴圈是 遍歷物件的每一個可列舉屬性,包括原型鏈上面的可列舉屬性;
  2. 而Object.keys()只是遍歷自身的可列舉屬性,不可以遍歷原型鏈上的可列舉屬性. 這是for...in和Object.keys()的主要區別;
  3. Object.getOwnPropertyNames()則是遍歷自身所有屬性(不論是否是可列舉的),不包括原型鏈上面的。