對象的屬性除了包含名字和值以外,屬性還包含了寫標識它們可寫,可枚舉和可配置的特性es6
ES5後,對於屬性的三大特性,js提供對應的API。經過這些API給原型對象添加方法,並將它們設置成不可枚舉的,這讓它們看上去更像內置方法。api
能夠經過這些api給對象定義不能修改或刪除的屬性,藉此封鎖這個對象。數組
數據屬性的4個特性分別是:bash
1.值(value)函數
2.可寫性(writable)ui
3.可枚舉性(enumerable)spa
4.可配置性(configurable)prototype
存取器屬性的4個特性code
1.讀取(get)對象
2.寫入(set)
3.可枚舉性(enumerable)
4.可配置性(configurable)
ES5定義了一個對象名爲「屬性描述符」(property descriptor)來表明對象屬性的特性。數據屬性的描述符對象的屬性有value,writable,enumerable , configurable,存取器屬性的描述符對象則用get和set屬性代替value和writable。其中enumerable,configurable,writable是布爾值,set和get屬性是函數值。
經過調用Object.getOwnPropertyDescriptior()能夠得到某個對象特定屬性的屬性描述符。該API只能獲得自由屬性的描述符。要想得到對象集成對象屬性的特性,須要遍歷原型鏈(Object.getPrototypeOf()).若想設置屬性的特性或者想讓新建的屬性具備某種特性,則須要調用Object.defineProperty()
Object.defineProperty(O,"x",{
value:1, writable:true, enumerable:false, configurable:true
})
複製代碼
對象的每一個屬性都有一個描述對象(Descriptor),用來控制該屬性的行爲。Object.getOwnPropertyDescriptor
方法能夠獲取該屬性的描述對象。
let obj = { foo: 123 };
Object.getOwnPropertyDescriptor(obj, 'foo')
// {
// value: 123,
// writable: true,
// enumerable: true,
// configurable: true
// }
複製代碼
描述對象的enumerable
屬性,稱爲」可枚舉性「,若是該屬性爲false
,就表示某些操做會忽略當前屬性。
目前,有四個操做會忽略enumerable
爲false
的屬性。
for...in
循環:只遍歷對象自身的和繼承的可枚舉的屬性。Object.keys()
:返回對象自身的全部可枚舉的屬性的鍵名。JSON.stringify()
:只串行化對象自身的可枚舉的屬性。Object.assign()
: 忽略enumerable
爲false
的屬性,只拷貝對象自身的可枚舉的屬性。這四個操做之中,前三個是 ES5 就有的,最後一個Object.assign()
是 ES6 新增的。其中,只有for...in
會返回繼承的屬性,其餘三個方法都會忽略繼承的屬性,只處理對象自身的屬性。實際上,引入「可枚舉」(enumerable
)這個概念的最初目的,就是讓某些屬性能夠規避掉for...in
操做,否則全部內部屬性和方法都會被遍歷到。好比,對象原型的toString
方法,以及數組的length
屬性,就經過「可枚舉性」,從而避免被for...in
遍歷到。
Object.getOwnPropertyDescriptor(Object.prototype, 'toString').enumerable
// false
Object.getOwnPropertyDescriptor([], 'length').enumerable
// false
複製代碼
上面代碼中,toString
和length
屬性的enumerable
都是false
,所以for...in
不會遍歷到這兩個繼承自原型的屬性。
另外,ES6 規定,全部 Class 的原型的方法都是不可枚舉的。
Object.getOwnPropertyDescriptor(class {foo() {}}.prototype, 'foo').enumerable
// false
複製代碼
總的來講,操做中引入繼承的屬性會讓問題複雜化,大多數時候,咱們只關心對象自身的屬性。因此,儘可能不要用for...in
循環,而用Object.keys()
代替。