从ES5开始,JavaScript开始支持所有属性都具备属性描述符。
var myObject = {
a: 2
};
Object.getOwnPropertyDescriptor(myObject, "a");
//{
// value: 2
// writable: true,
// enumerable: true,
// configurable: true
//}
这个普通的对象属性对应的属性描述符有四个:value、writable、enumerable、configurable,其中value标识属性值,其他三个用来描述属性是否可写、可枚举、可配置。
在创建属性时属性描述符会使用默认值,也可以通过使用Object.defineProperty()方法添加新属性或修改一个已有属性的描述符。
Object.defineProperty(myObject, "a", {
value: 3,
writable: true,
configurable: true,
enumerable: false
})
1、writable
writable决定是否可以修改属性的值。
var myObject = {};
Object.defineProperty(myObject, "a", {
value: 2,
writable: false, // 不可写
configurable: true,
enumerable: true
});
myObject.a = 3; // 如果是严格模式,会抛出TypeError
myObject.a; // 2,修改属性a的值失败
javascript中Object.freeze()方法会创建一个冻结的对象,使对象的所有直接属性禁止修改,实际上该方法调用了Object.seal()方法,然后设置所有的对象直接属性的wriable:false。
2、configurable
configurable决定属性是否是可配置的,如果是可配置的,可以使用defineProperty()方法修改属性描述符。
var myObject = {
a: 3
};
Object.defineProperty(myObject, "a", {
value: 4,
writable: true,
configurable: false, // 不可配置
enumerable: true
});
delete myObject.a; // 删除myObject.a
myObject.a; // 2 删除失败
// 再次调用defineProperty()方法会抛出TypeError错误
Object.defineProperty(myObject, "a", {
value: 6,
writable: true,
configurable: true,
enumerable: true
}); // TypeError
所以,一旦把属性的configurable修改为false就无法撤销,此操作是单向操作,并且该属性也无法被delete操作删除
javascript中有一个方法Object.seal(),会创建一个不能添加新属性,也不能重新配置或删除任何现有属性的“密封”对象。该方法实际上会调用Object.preventExtensions()方法,并把所有现有属性标记为configurable:false。其中Object.preventExtensions()方法会禁止向对象添加新属性且保留已有属性。
3、enumerable
enumerable控制属性是否会出现在对象的属性枚举中。比如for…in循环。如果设置为false,该属性就不会出现在枚举中。
var myObject = {};
Object.defineProperty(myObject, "a", {
value: 2,
enumerable: true
});
Object.defineProperty(myObject, "b", {
value: 3,
enumerable: false
});
myObject.b; // 3
("b" in myObject); // true
myObject.hasOwnProperty("b"); // true
for(var k in myObject) {
console.log(k, myObject[k]);
}
// "a" 2
可以看到,myObject.b确实存在且有访问值,但却不会出现在for…in循环中。原因就是myObject.b的enumerable:false导致的。
4、对象常量
结合writable:false和configurable:false可以创建一个真正的常量属性。
var myObject = {}
Object.defineProperty(myObject, "CONSTANT", {
value: 42,
writable: false,
configurable: false
});
5、Getter和Setter
在ES5中可以使用getter和setter改写属性的默认操作。getter和setter都是隐藏函数,getter会在获取属性值时调用,setter会在设置属性值时调用。
当给一个属性定义了getter、setter时,JavaScript会忽略他们的value和writable特性,取而代之的是get和set特性。
var myObject = {
// 给a定义一个getter
get a() {
return 2;
}
};
Object.defineProperty(myObject, "b", {
get: function() {return this.a * 2},
enumerable: ture
});
myObject.a; // 2
myObject.b; // 4
myObject.a = 3;
myObject.a; // 2
无论使用get a() {..}, 还是defineProperty()定义getter,二者都会在对象中创建一个不包含值的属性,对于该属性的访问会自动调用一个隐藏函数,该函数的返回值被当作属性访问的返回值。
由于没有为属性a定义setter,所以对属性a的赋值操作会失败。但是由于a属性的自定义getter只会返回2,所以即使定义了setter,赋值操作也是没有意义的。
接下来在看一个有合理setter和getter的对象属性:
var myObject = {
get a() {
return this._a_;
},
set a(val) {
this._a_ = val * 2;
}
};
myObject.a = 2;
myObject.a; // 4