JavaScript 提供了一個內部數據結構,用來描述對象的屬性,控制它的行爲,好比該屬性是否可寫、可枚舉等等。這個內部數據結構稱爲「屬性描述對象」(attributes object
)。每一個屬性都有本身對應的屬性描述對象,保存該屬性的一些元信息。javascript
屬性描述對象的各個屬性稱爲「元屬性」,由於它們能夠看做是控制屬性的屬性。html
value
屬性是目標屬性的值。java
var obj = { p : 123 }; Object.getOwnPropertyDescriptor(obj, 'p').value // 123 Object.defineProperty(obj, 'p', { value: 246 }); obj.p // 246
改寫 value
屬性時,只要 writable
和 configurable
有一個爲 true
,就容許改動。只有 writable
和 configurable
兩個都爲 false
時,value
屬性纔不可改寫。數據結構
var obj = Object.defineProperty({}, 'p', { value: 1, writable: false, configurable: false }); Object.defineProperty(obj, 'p', {value: 2}) // TypeError: Cannot redefine property: p obj.p = 4 obj.p // 1
value
屬性不可改寫時,直接屬性賦值,不報錯,但不會成功。在嚴格模式下會報錯,即便對屬性從新賦予一個一樣的值。函數
writable
屬性是一個布爾值,決定了目標屬性的值 value
是否能夠被改變。this
var obj = {}; Object.defineProperty(obj, 'a', { value: 37, writable: false }); obj.a // 37 obj.a = 25; obj.a // 37
若是原型對象的某個屬性的 writable
爲 false
,那麼子對象將沒法自定義這個屬性。prototype
var proto = Object.defineProperty({}, 'foo', { value: 'a', writable: false }); var obj = Object.create(proto); obj.foo = 'b'; obj.foo // 'a'
可是,有一個規避方法,就是經過覆蓋屬性描述對象,繞過這個限制。緣由是這種狀況下,原型鏈會被徹底忽視。code
Object.defineProperty(obj, 'foo', { value: 'b' }); obj.foo // "b"
enumerable
(可枚舉性)屬性是一個布爾值,表示目標屬性是否可枚舉。htm
若是一個屬性的 enumerable
爲 false
時,下面四個操做將不會取到該屬性。對象
for..in
循環Object.keys()
方法JSON.stringify()
方法Object.assign()
:只拷貝對象自身的可枚舉的屬性。var obj = {}; Object.defineProperty(obj, 'x', { value: 123, enumerable: false }); obj.x // 123 for (var key in obj) {console.log(key);} // undefined Object.keys(obj) // [] JSON.stringify(obj) // "{}"
JSON.stringify
方法會排除 enumerable
爲 false
的屬性,若是對象的 JSON 格式輸出要排除某些屬性,就能夠利用這一點把這些屬性的 enumerable
設爲 false
。
configurable
(可配置性)返回一個布爾值,決定了是否能夠修改屬性描述對象。也就是說,configurable
爲 false
時,value
、writable
、enumerable
和 configurable
都不能被修改了。
var obj = Object.defineProperty({}, 'p', { value: 1, writable: false, enumerable: false, configurable: false }); Object.defineProperty(obj, 'p', {value: 2}) // TypeError: Cannot redefine property: p Object.defineProperty(obj, 'p', {writable: true}) // TypeError: Cannot redefine property: p Object.defineProperty(obj, 'p', {enumerable: true}) // TypeError: Cannot redefine property: p Object.defineProperty(obj, 'p', {configurable: true}) // TypeError: Cannot redefine property: p
注意,writable
只有在 false
改成 true
時會報錯,true
改成 false
是容許的。
var obj = Object.defineProperty({}, 'p', { writable: true, configurable: false }); Object.defineProperty(obj, 'p', {writable: false}) // 修改爲功
可配置性決定了目標屬性是否能夠被刪除(delete
)。configurable
爲 true
時,屬性能夠被刪除,爲 false
時,屬性不可被刪除。
var obj = Object.defineProperties({}, { p1: { value: 1, configurable: true }, p2: { value: 2, configurable: false } }); delete obj.p1 // true delete obj.p2 // false obj.p1 // undefined obj.p2 // 2
除了直接定義之外,屬性還能夠用存取器(accessor)定義。其中,存值函數稱爲 setter
,使用屬性描述對象的 set
屬性;取值函數稱爲 getter
,使用屬性描述對象的 get
屬性。
一旦對目標屬性定義了存取器,那麼存取的時候,都將執行對應的函數。利用這個功能,能夠實現許多高級特性,好比某個屬性禁止賦值。
var obj = Object.defineProperty({}, 'p', { get: function () { return 'getter'; }, set: function (value) { console.log('setter: ' + value); } }); obj.p // "getter" obj.p = 123 // "setter: 123"
一旦定義了取值函數 get
或存值函數 set
,就不能同時定義 writable
屬性或 value
屬性,不然會報錯。若是經過 Object.defineProperty()
重定義 writable
屬性或 value
屬性,那麼取值函數 get
和存值函數 set
將會被 value
和 writable
覆蓋。
var obj = Object.defineProperty({}, 'p', { get: function () { return 'getter'; }, set: function (value) { console.log('setter: ' + value); }, configurable: true }); Object.getOwnPropertyDescriptor(obj,'p') // { get: ƒ (), // set: ƒ (value), // enumerable: false, // configurable: true // } // 重定義 Object.defineProperty(obj, 'p', { writable:true }); Object.getOwnPropertyDescriptor(obj,'p') // {value: undefined, writable: true, enumerable: false, configurable: true}
JavaScript 還提供了存取器的另外一種寫法。與定義屬性描述對象是等價的,並且使用更普遍。
var obj = { get p() { return 'getter'; }, set p(value) { console.log('setter: ' + value); } };
注意,取值函數 get
不能接受參數,存值函數 set
只能接受一個參數(即屬性的值)。
存取器每每用於,屬性的值依賴對象內部數據的場合。
var obj ={ $n : 5, get next() { return this.$n }, set next(n) { if (n >= this.$n) this.$n = n; else throw new Error('新的值必須大於當前值'); } }; obj.next // 5 obj.next = 10; obj.next // 10 obj.next = 5; // Uncaught Error: 新的值必須大於當前值
Object.getOwnPropertyDescriptor()
能夠獲取某個屬性的屬性描述對象。它的第一個參數是對象,第二個參數是對象的某個屬性名。返回的是該屬性的屬性描述對象。
var obj = { p1: 'a', p2: 'b'}; Object.getOwnPropertyDescriptor(obj, 'p1') // { value: "a", // writable: true, // enumerable: true, // configurable: true // }
只能用於對象自身的(非繼承的)屬性。繼承的或不存在的屬性返回 undefined
。
Object.getOwnPropertyDescriptor(obj, 'toString') // undefined
Object.getOwnPropertyDescriptors()
能夠獲取參數對象的全部屬性的屬性描述對象。ES2017 引入標準。
Object.getOwnPropertyDescriptors(obj) // { p1: {value: "a", writable: true, enumerable: true, configurable: true} // p2: {value: "b", writable: true, enumerable: true, configurable: true} // }
Object.defineProperty()
方法容許經過屬性描述對象,定義或修改一個屬性,而後返回修改後的描述對象。
Object.defineProperty(object, propertyName, attributesObject)
Object.defineProperty()
方法接受三個參數,依次以下。
object
:屬性所在的對象propertyName
:字符串,表示屬性名attributesObject
:屬性描述對象var obj = Object.defineProperty({}, 'p', { value: 123, writable: false, enumerable: true, configurable: false }); obj.p // 123 obj.p = 246; obj.p // 123
注意,上例中第一個參數是{ }(一個新建的空對象),p屬性直接定義在這個空對象上面,而後返回這個對象,這是 Object.defineProperty()
的常見用法。
若是屬性已經存在,Object.defineProperty()
方法至關於更新該屬性的屬性描述對象。
Object.defineProperties()
方法能夠定義或修改多個屬性。接受兩個參數。
var obj = Object.defineProperties({}, { p1: { value: 123, enumerable: true }, p2: { value: 'abc', enumerable: true }, p3: { get: function () { return this.p1 + this.p2 }, enumerable:true, configurable:true } }); obj.p1 // 123 obj.p2 // "abc" obj.p3 // "123abc"
元屬性默認值
Object.defineProperty()
和 Object.defineProperties()
參數裏面的屬性描述對象,writable
、configurable
、enumerable
這三個屬性的默認值都爲 false
。
var obj = {}; Object.defineProperty(obj, 'foo', {}); Object.getOwnPropertyDescriptor(obj, 'foo') // { // value: undefined, // writable: false, // enumerable: false, // configurable: false // }
實例對象的 propertyIsEnumerable()
方法返回一個布爾值,用來判斷某個屬性是否可枚舉。
var obj = {}; obj.p = 123; obj.propertyIsEnumerable('p') // true obj.propertyIsEnumerable('toString') // false
注意,這個方法只能用於判斷對象自身的屬性,對於繼承的屬性一概返回 false
。
參考連接:JavaScript 教程 屬性描述對象