什么是枚舉?枚舉是指對象中的屬性是否可以遍歷出來,再簡單點說就是屬性是否可以以列舉出來。
一、怎么判斷屬性是否可枚舉
在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()則是遍歷自身所有屬性(不論是否是可枚舉的),不包括原型鏈上面的。