属性描述符用于描述对象的值是否可配置、是否可修改、是否可枚举。
描述符类型
对象的属性描述符的类型分为两种:数据属性和访问器属性。
数据属性
数据属性包含一个数据值的位置,该位置可读取和写入值。数据属性有4个特性。
【configurable】configurable表示可配置性,它决定了是否可以用delete操作符删除属性,以及是否可以修改属性描述符的特性,默认值true。
【enumerable】enumerable表示可枚举性,它决定了是否可以用for-in循环返回该属性,默认值true。
【writable】writable表示可写性,它决定了是否可以修改属性值,默认值true。
【value】value表示属性的数据值,读取属性值的时候就是从这个位置读;写入属性值的时候新值保存在该位置。默认值undefined。
访问器属性
对象属性是由名字、值和属性描述符构成的。属性值可以用一个或两个方法替代,这两个方法是getter和setter。这种属性类型叫做访问器属性。访问器属性有4个特性。
【configurable】configurable表示可配置性,它决定了是否可以用delete操作符删除属性,以及是否可以修改属性描述符的特性,默认值true。
【enumerable】enumerable表示可枚举性,它决定了是否可以用for-in循环返回该属性,默认值true。
【getter】读取属性时调用的函数。默认值为undefined。
【setter】写入属性时调用的函数。默认值为undefined。
如果属性同时具有getter和setter方法,它是一个可读写属性。只有getter方法是一个只读属性,只有setter方法是一个只写属性。
描述符方法
描述符方法用于设置属性描述符。描述符方法有4个。
Object.getOwnPropertyDescriptor()
Object.getOwnPropertyDescriptor(obj, name)方法用于查询对象一个属性的描述符,查询结果以对象的形式返回。
var obj = {a: 1}
console.log(Object.getOwnPropertyDescriptor(obj, 'a')) // {value: 1, writable: true, enumerable: true, configurable: true}
// 属性不存在,返回undefined
console.log(Object.getOwnPropertyDescriptor(obj, 'b')) // undefined
Object.defineProperty()
Object.defineProperty(obj, name, descriptor)方法用于创建或配置对象的一个属性描述符,返回配置后的对象。
注意: 该方法创建或配置对象属性的描述符时,如果不针对该属性进行描述符的配置,则该项描述符默认为false。
var obj = {}
Object.defineProperty(obj, 'a', {
value: 1,
writable: true
})
console.log(Object.getOwnPropertyDescriptor(obj, 'a')) // {value: 1, writable: true, enumerable: false, configurable: false}
Object.defineProperties()
Object.defineProperties(obj, descriptors)方法用于创建或配置对象的多个描述符,返回配置后的对象。
var obj = {a: 1}
Object.defineProperties(obj, {
a:{writable: false},
b:{value: 2}
})
console.log(Object.getOwnPropertyDescriptor(obj, 'a')) // {value: 1, writable: false, enumerable: true, configurable: true}
console.log(Object.getOwnPropertyDescriptor(obj, 'b')) // {value: 2, writable: false, enumerable: false, configurable: false}
Object.create()
Object.create(proto, descriptors)方法使用指定的原型对象和属性创建一个对象。
var obj = {a: 1}
var o = Object.create(obj,{
b:{value: 2}
})
console.log(Object.getOwnPropertyDescriptor(o, 'a')) // undefined
console.log(Object.getOwnPropertyDescriptor(o, 'b')) // {value: 2, writable: false, enumerable: false, configurable: false}
详述属性描述符
接下来对上面提到的数据属性描述符和访问器属性描述符进行逐一详细说明。
writable
writable设置为false后,赋值语句会失效,严格模式下赋值会报错。
var obj = {a: 1}
Object.defineProperty(obj, 'a', {
writable: false
})
obj.a = 2
console.log(obj.a) // 1
writable设置为false后,可以通过Object.defineProperty()方法改变属性value的值。
var obj = {a: 1}
Object.defineProperty(obj, 'a', {
writable: false
})
Object.defineProperty(obj, 'a', {
value: 2
})
console.log(obj.a) // 2
configurable
configurable设置为false后,无法使用delete删除属性,严格模式下删除报错。
var obj = {a: 1}
Object.defineProperty(obj,'a',{
configurable:false
});
delete obj.a
console.log(obj.a) // 1
configurable设置为false后,无法再使用defineProperty()方法设置configurable为true、enumerable为false、writable为true。
var obj = {a: 1}
Object.defineProperty(obj,'a',{
configurable:false
});
// 示例1
Object.defineProperty(obj,'a',{
configurable:true
}); // 类型错误
// 示例2
Object.defineProperty(obj,'a',{
enumerable:false
}); // 类型错误
// 示例3
Object.defineProperty(obj,'a',{
writable:true
});// 类型错误
使用var声明变量时,变量的configurable是false。
var a = 1;
// { value: 1, writable: true, enumerable: true, configurable: false}
Object.getOwnPropertyDescriptor(this,'a');
enumerable
enumerable设置为false后,无法使用for/in、Object.keys、JSON.stringify等方法获取该属性。
var obj = {a: 1}
// 原生继承的属性默认不可枚举,普通属性可枚举,所以只获取到a:1
for(var i in obj) {
console.log(obj[i]) // 1
}
// enumerable被设置为false,a属性无法被枚举。
Object.defineProperty(obj,'a',{enumerable:false});
for(var i in obj){
console.log(o[i]);//undefined
}
propertyIsEnumerable()方法用于判断对象的属性是否可枚举。
var obj = {a:1};
console.log(obj.propertyIsEnumerable('a'));//true
Object.defineProperty(obj,'a',{enumerable:false});
console.log(obj.propertyIsEnumerable('a'));//false
get和set
get是一个隐形函数,在获取属性值时调用。set也是一个隐形函数,在设置属性值时调用。它们默认值都是undefined。
Object.definedProperty()中的get和set方法对应对象字面量的get和set方法。
注意 getter和setter取代了数据属性中的value和writable属性
- 如果只设置了get方法,为对象赋值会静默失败,严格模式下报错。
// 示例1
var o = {
get a() {
return 1
}
}
o.a = 2
console.log(o.a) // 1
// 示例2
var o = {};
Object.defineProperty(o, 'a', {
configurable: true,
enumerable: true,
get: function() {
return 1
}
})
o.a = 2
console.log(o.a) // 1
- 如果只设置了set方法,读取对象的属性值会返回undefined。
// 示例1
var o = {
set a(val) {
return val
}
}
o.a = 1
console.log(o.a) // undefined
// 示例2
var o = {};
Object.defineProperty(o, 'a', {
configurable: true,
enumerable: true,
set: function(val) {
return val
}
})
o.a = 1
console.log(o.a) // undefined
- 通常set和get方法成对出现。
// 示例1
var o = {
get a() {
return this._a
},
set a(val) {
this._a = val
}
}
o.a = 1
console.log(o.a) // 1
// 示例2
var o = {};
Object.defineProperty(o, 'a', {
configurable: true,
enumerable: true,
get: function() {
return this._a
},
set: function(val) {
this._a = val
}
})
o.a = 1
console.log(o.a) // 1
对象状态
属性描述符只能控制对象中一个属性的状态,下面介绍6种方法用于控制整个对象的状态。
Object.preventExtensions()禁止扩展
Object.preventExtensions()方法使对象无法添加新的属性,并返回当前对象,它不会改变对象中属性描述符的状态。严格模式下给禁止扩展的对象添加属性会报错。
Object.isExtensible()测试扩展
Object.isExtensible()方法检测对象是否可以扩展。
var o = {a: 1}
console.log(Object.isExtensible(o)) // true
Object.preventExtensions(o)
o.b = 2
console.log(Object.isExtensible(o)) // false
console.log(o) // {a: 1}
console.log(Object.getOwnPropertyDescriptor(o, 'a')) // {value: 1, writable: true, enumerable: true, configurable: true}
Object.seal()对象封印,也叫对象密封
Object.seal()方法使一个对象不可扩展并且不可配置,返回当前对象。严格模式下删除旧属性或添加新属性都会报错。
Object.isSealed()测试封印
Object.isSealed()方法检测对象是否被封印。
var o = {a:1}
console.log(Object.isSealed(o)) // false
Object.seal(o)
console.log(Object.isSealed(o)) // true
delete o.a
o.b = 2
console.log(o) // {a: 1}
这个方法实际上是在现有对象上调用Object.preventExtensions()方法,并把所有现有属性的configurable描述符设置为false。
var o = {a: 1}
Object.seal(o)
console.log(Object.getOwnPropertyDescriptor(o,'a')) // {value: 1, writable: true, enumerable: true, configurable: false}
Object.frozen()对象冻结
Object.frozen()方法使一个对象不可扩展、不可配置且不可改写,变成一个只可枚举的只读常量,并返回当前对象。严格模式下删除旧属性、添加新属性、更改现有属性都会报错。
Object.isFrozen()检测冻结
Object.isFrozen()方法检测一个对象是否被冻结
var o = {a: 1}
console.log(Object.isFrozen(o)) // false
Object.frozen(o)
console.log(Object.isFrozen(o)) // true
o.a = 2
console.log(o.a) // {a: 1}
这个方法实际上是在现有对象上调用Object.writable()方法,并把所有现有属性的writable描述符设置为false。
var o = {a:1}
Object.freeze(o)
console.log(Object.getOwnPropertyDescriptor(o,'a')); //{value: 1, writable: false, enumerable: true, configurable: false}