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