一、數據屬性描述符
對象是一個屬性集合,對象的基本特征是屬性名(name)和屬性值(value)。ES5 增加了屬性描述符,可以更細膩的控制屬性的不同操作。屬性描述符有 configurable、writable 和 enumerable。
屬性描述符通常和 Object.defineProperty/Object.defineProperties 一起使用來定義屬性,它也會受到諸如 Object.freeze/Object.seal 等方法改變。
1. configurable 當且僅當 configurable 為 true 時,該屬性才能夠被改變,也能夠被刪除(delete),默認為 false
var obj = {}
Object.defineProperty(obj, 'name', {
value: 'John'
})
// 不能 delete
delete obj.name // false
Object.defineProperty(obj, 'name', {
configurable: true,
value: 'John'
})
// 可以delete
delete obj.name // true
2. writable 當且僅當 writable 為 true 時,該屬性才能被賦值運算符(=)改變,默認為 false
var obj = {}
Object.defineProperty(obj, 'name', {
value: 'John'
})
obj.name = 'Backus' // 修改不起作用,仍然是 John,嚴格模式中會報錯阻止修改
Object.defineProperty(obj, 'name', {
writable: true,
value: 'John'
})
obj.name = 'Backus' // 被改為了 backus
3. enumerable 當且僅當 enumerable 為 true 時,該屬性才能夠出現在對象的枚舉屬性(for in)中,默認為 false
var obj = {}
Object.defineProperty(obj, 'name', {
value: 'John'
})
// 不能遍歷
for (var a in obj) {
console.log(a) // 無輸出
}
Object.defineProperty(obj, 'name', {
enumerable: true,
value: 'John'
})
// 可以遍歷
for (var a in obj) {
console.log(a) // 輸出 "name"
}
ES6 的 Object.keys 只返回 enumerable=true 的屬性
var obj = {name: 'John'}
Object.defineProperty(obj, 'name', {
value: 'Backus',
enumerable: true
})
Object.defineProperty(obj, 'age', {
value: 30,
enumerable: false
})
Object.keys(obj) // ['name']
可以通過 propertyIsEnumerable 方法判斷屬性的 enumerable 值
obj.propertyIsEnumerable('name') // true
obj.propertyIsEnumerable('age') // false
4. 使用 ES3(傳統的) JSON 方式定義對象,其 configurable/writable/enumerable 默認都是 true,如下
var obj = {name: 'John', age: 30}
// configurable
delete obj.name // true
// writable
obj.age = 32 // true
// enumerable
for (var a in obj) {
console.log(a) // age
}
也即
var obj = {name: 'John', age: 30}
等同於
Object.defineProperty(obj, 'name', {
value: 'John',
configurable: true,
writable: true,
enumerable: true
})
Object.defineProperty(obj, 'age', {
value: 33,
configurable: true,
writable: true,
enumerable: true
})
5. 使用 ES5 的 Object.defineProperty/Object.defineProperties 方式定義對象,其 configurable/writable/enumerable 默認都是 false,如下
var obj = {}
Object.defineProperty(obj, 'name', {
value: 'John'
})
Object.defineProperty(obj, 'age', {
value: 33
})
// configurable
delete obj.name // false
// writable
obj.age = 32 // false
// enumerable
for (var a in obj) {
console.log(a) // 無輸出,不能遍歷
}
也即
Object.defineProperty(obj, 'name', {
value: 'John'
})
等同於
Object.defineProperty(obj, 'name', {
value: 'John',
configurable: false,
writable: false,
enumerable: false
})
數據屬性描述符匯總如下

二、存取屬性描述符
存取描述符是由一對 getter-setter 函數功能來描述的屬性,格式為
name: {
get: function() { ... },
set: function(newVal) { ... },
enumerable: true,
configurable: true
}
例如
var obj = {}
Object.defineProperty(obj, 'name', {
configurable: true,
enumerable: true,
get: function() {
console.log('get')
return this.value
},
set: function(newVal) {
console.log('set')
this.value = newVal
}
})
// 賦值會調用 set 方法
obj.name = 'John'
// 取值會調用 get 方法
obj.name
與上述的屬性描述符只能存在一種,即二選一,不能同時存在,否則會報錯
var obj = {}
// 錯誤方式一
Object.defineProperty(obj, 'name', {
value: 'John',
get: function() {
console.log('get')
return this.value
}
})
// 錯誤方式二
Object.defineProperty(obj, 'name', {
writable: true,
get: function() {
console.log('get')
return this.value
}
})
Firefox 報錯如下

存取描述符匯總如下

三、和屬性描述符相關的幾個函數
- Object.defineProperty
- Object.defineProperties
- Object.getOwnPropertyDescriptor
Object.defineProperty 上面已經介紹過,Object.defineProperties 批量定制對象屬性,內部其實循環方式調用 Object.defineProperty
Object.defineProperties(obj, {
name: {
value: 'John',
writable: true
},
age: {
value: 30,
enmuerable: true
}
})
Object.getOwnPropertyDescriptor 返回該對象某屬性的描述器,描述器自身是一個對象
var obj = {}
Object.defineProperty(obj, 'name', {
value: 'Backus',
writable: true,
enumerable: true
})
var des = Object.getOwnPropertyDescriptor(obj, 'name')
console.log(des)
輸出如圖

