本文主要学习一下 JavaScript 中对象的属性描述符。
从 ES5 开始,所有的属性都具备了属性描述符。下面是查看和配置属性描述符的方式:
var obj = {
a: ''
}
Object.getOwnPropertyDescriptor(obj, 'a');
// {
// configurable: true,
// enumerable: true,
// value: "",
// writable: true
// }
Object.defineProperty(obj, 'b', {
configurable: true,
enumerable: true,
value: "",
writable: true
})
// { a: '', b: '123'}
属性描述一共四个属性,分别来认识一下:
configurable
只要属性的 configurable 是 true,就可以使用 defineProperty(..) 方法来修改属性描述符。
var myObject = {
a: 2
};
myObject.a = 3;
myObject.a; // 3
Object.defineProperty(myObject, "a", {
value: 4,
writable: true,
configurable: false, // 不可配置!
enumerable: true
});
myObject.a; // 4
myObject.a = 5;
myObject.a; // 5
Object.defineProperty(myObject, "a", {
value: 6,
writable: true,
configurable: true,
enumerable: true
}); // TypeError
delete myObject.a;
myObject.a // 5
可以看到,将 configurable 设为 false 后将无法再次使用 Object. defineProperty ()
配置,会报 TypeError 异常,所以这个行为是不可逆的。
而且也无法使用 delete 删除 configurable 为 false 的对象属性。
enumerable
这个描述符控制的是属性是否会出现在对象的属性枚举中,如 for...in... 循环。
var myObject = {
a: 2,
b: 3,
c: 7,
d: 12
};
Object.defineProperty(myObject, "e", {
value: 4,
writable: true,
configurable: true,
enumerable: false // 不可枚举!
});
for (var key in myObject) {
console.log(key)
}
// a
// b
// c
// d
myObject.e // 4
上面代码中,e 属性可以直接访问,但是并没有出现在 for...in 遍历中。
value
value 就是对象属性的值,这点很容易理解。
var myObject = {
a: 2
};
Object.getOwnPropertyDescriptor(myObject, 'a');
// {
// value: 2,
// writable: true,
// configurable: true,
// enumerable: true
// }
console.log(myObject.a) // 2
Object.defineProperty(myObject, "a", {
value: 4,
writable: true,
configurable: true,
enumerable: true
});
console.log(myObject.a) // 4
writable
writable 决定了是否可以修改属性的值。
var myObject = {};
Object.defineProperty(myObject, "a", {
value: 2,
writable: false, // 不可写!
configurable: true,
enumerable: true
});
myObject.a = 3;
myObject.a; // 2
上面代码中,无法对属性 a 的值进行修改。
不变性
说完了属性描述符,顺便说下对象属性的不变性。
1. 对象常量
通过属性描述符 writable 和 configurable 可以创建一个不可修改、重定义和删除的常量属性。
var myObject = {};
Object.defineProperty(myObject, "FAVORITE_NUMBER", {
value: 42,
writable: false,
configurable: false
});
myObject.FAVORITE_NUMBER = 33 // 无效
delete myObject.FAVORITE_NUMBER // 无效
Object.defineProperty(myObject, "FAVORITE_NUMBER", {
value: 33
}); // TypeError
2. 静止扩展
禁止对象添加新属性(不可扩展),可以使用 Object.preventExtensions(...) 方法。
var myObject = {
a: 2
};
Object.preventExtensions(myObject);
myObject.b = 3;
myObject['c'] = 77
myObject.b; // undefined
myObject.c; // undefined
myObject.a = 3
myObject // { a: 3 }
delete myObject.a
myObject // {}
这个方法只保证了对象不可扩展性新属性,已有属性可以修改也可以删除。
3. 密封
Object.seal(...) 会创建一个密封对象,等于 Object.preventExtensions(...) 加上 configurable: false 的效果。
var obj = {
a: 1
}
Object.seal(obj)
obj.b = 3 // { a: 1 }
delete obj.a // { a: 1 }
obj.a = 7 // { a: 7 }
Object.defineProperties(obj, 'a', {
value: 14
}) // TypeError
冻结
Object.freeze(...) 会创建一个冻结对象,它等于 Object.seal(...) 加上 writable:false 属性。
var obj = {
a: 1
}
Object.freeze(obj)
obj.b = 3 // { a: 1 }
delete obj.a // { a: 1 }
obj.a = 7 // { a: 1 }
Object.defineProperties(obj, 'a', {
value: 14
}) // TypeError
最后
明天一起来聊聊对象的 setter getter 和 proxy~