属性描述符
在 ES5 开始,所有属性都具备了属性描述符。
我们可以通过 Object.getOwnPropertyDescriptor 来观察到属性的描述符,它是长这样子的。
Object.getOwnPropertyDescriptor({a: 1}, ‘a’);
// {
// value: 1,
// writable: true,
// enumerable: true,
// configurable: true
// }
writable 决定该属性是否只读。enumerable 决定该属性是否可枚举。configurable 决定该属性是否可重新设置描述符。
属性描述符的设置
当属性的 configurable 为 true 时,我们可以通过 Object.defineProperty 来修改属性描述符。
‘use strict’;
var foo = {a: 1};
for(var i in foo) {console.log(i)};
// ‘a’
Object.defineProperty(foo, ‘a’, {
writable: false,
enumerable: false,
configurable: false
});
foo.a = 2;// Uncaught TypeError: Cannot assign to read only property ‘a’ of object ‘#<Object>’
for(var i in foo) {console.log(i)};
// ‘nothing happend’
Object.defineProperty(foo, ‘a’, { // Uncaught TypeError: Cannot redefine property: a
configurable: true
});
访问描述符(getter 和 setter)
getter 和 setter 有两种方式定义
字面量定义
var foo = {
get a(){
return this._a;
},
set a(value){
this._a = value;
}
};
使用 Object.defineProperty 定义
var foo = {a: 1};
Object.defineProperty(
foo,
‘a’,
{
get(){
return this._a;
},
set(value){
this._a = value;
}
}
);
这个时候再看看属性 a 的描述符。
Object.getOwnPropertyDescriptor(foo, ‘a’)
// {
// get: ƒ a(),
// set: ƒ a(value),
// enumerable: true,
// configurable: true
// }
当出现 getter 或 setter 时,value 和 writable 就会失效。此时,属性 a 的描述符被称为访问描述符。访问描述符和属性描述符互斥,如果此时再重新设置 value 或者 writable 描述符,setter 和 getter 也会被丢弃。
屏蔽属性
当你给一个对象赋值一个新的属性 foo 时,如果该对象的原型链上已存在属性 foo,并且 foo 被标记为只读 (writable: false) 时,严格模式下会抛出异常,非严格模式下,这条赋值语句会被忽略。这种属性称为屏蔽属性。
举个例子
‘use strict’;
var anotherObject = {};
Object.defineProperty(anotherObject, ‘foo’, {// 将 anotherObject 的 foo 属性设为只读
value: 1,
writable: false
});
var myObject = Object.create(anotherObject);// 将 myObject 的原型设置为 anotherObject
console.log(myObject);// {}
console.log(myObject.foo);// 1
myObject.foo = 2;// Uncaught TypeError: Cannot assign to read only property ‘a’ of object ‘#<Object>’
还有一种情况
当你给一个对象赋值一个新的属性 foo 时,如果该对象的原型链上已存在属性 foo,并且 foo 被设置了 setter 时,将会调用这个 setter,并且该赋值语句将会被忽略,此时也会发生属性屏蔽。
var anotherObject = {};
Object.defineProperty(anotherObject, ‘foo’, {// 给 anotherObject 的 foo 属性设置 setter
set(value) {
console.log(value);
this._foo = value;
},
get() {
return this._foo;
}
});
var myObject = Object.create(anotherObject);// 将 myObject 的原型设置为 anotherObject
myObject.foo = 2;// 此时会触发 anotherObject.foo 的 setter,控制台输出 2
console.log(Object.hasOwnProperty(myObject, ‘foo’));// false
解决方案
使用 Object.defineProperty 来添加属性。