一般情况下,我们给对象添加属性值都是这样子的:
let obj = {};
obj.key = 123;
其实,还有一种添加属性的方式是:Object.defineProperty(obj, prop, descriptor),这个API非常重要,它是VUE等框架实现数据响应式的基石。利用它设置的访问器属性可以做到数据劫持,然后再通过订阅/发布模式将数据的变化反应到视图上。
不扯远了,本文先来单纯地探究一下 Object.defineProperty
定义属性
let obj = {};
Object.defineProperty(obj, "key", {
configurable: false,
enumerable: false,
writable: false,
value: 123
});
我们称 configurable、enumerable、writable、value 为属性的四个描述符(即descriptor),也可以称它们为属性的四个特性:
configurable
当且仅当该属性的 configurable 为 true 时,该属性的描述符才能够被改变,同时该属性也能从对应的对象上被删除。例子:// 为true时可以改变其他描述符,也可以删除这个属性
let obj = {};
Object.defineProperty(obj, "key", {
configurable: true,
enumerable: false,
writable: false,
value: 123
});
Object.defineProperty(obj, "key", {
enumerable: true
});
// 为false时不可以改变其他描述符,也不能删除这个属性
let obj = {};
Object.defineProperty(obj, "key", {
configurable: false,
enumerable: false,
writable: false,
value: 123
});
Object.defineProperty(obj, "key", {enumerable: true}); //报错
delete obj.key; // false
enumerable
当且仅当该属性的enumerable为true时,该属性才能被枚举。// 为true时属性可以被枚举
let obj = {};
Object.defineProperty(obj, "key", {
configurable: true,
enumerable: true,
writable: false,
value: 123
});
for (var i in obj) {
console.log(i); // key
}
// 为false时属性可以不被枚举
let obj = {};
Object.defineProperty(obj, "key", {
configurable: true,
enumerable: false,
writable: false,
value: 123
});
for (var i in obj) {
console.log(i); // undefined
}
writable
当且仅当该属性的writable为true时,value才能被赋值运算符改变。// 为true时属性可以被改变
let obj = {};
Object.defineProperty(obj, "key", {
configurable: true,
enumerable: true,
writable: true,
value: 123
});
obj.key = 456;
console.log(obj.key); // 456
// 为false时属性不可以被改变
let obj = {};
Object.defineProperty(obj, "key", {
configurable: true,
enumerable: true,
writable: false,
value: 123
});
obj.key = 456;
console.log(obj.key); // 123
value
该属性对应的值。可以是任何有效的 JavaScript 值(数值,对象,函数等)注意,如果用 Object.defineProperty 定义属性时没有主动设置描述符,那么所有的描述符的值为false。
我们用 Object.getOwnPropertyDescriptor(obj, "key") 来获取属性的描述符信息
// 例1
let obj = {};
Object.defineProperty(obj, "key", {});
Object.getOwnPropertyDescriptor(obj, "key");
<!--
{
configurable: false,
enumerable: false,
writable: false,
value: undefined
}
-->
// 例2
let obj = {};
Object.defineProperty(obj, "key", {value: 123});
Object.getOwnPropertyDescriptor(obj, "key");
<!--
{
configurable: false,
enumerable: false,
writable: false,
value: 123
}
-->
// 例3
let obj = {};
Object.defineProperty(obj, "key", {writable: true});
Object.getOwnPropertyDescriptor(obj, "key");
<!--
{
configurable: false,
enumerable: false,
writable: true,
value: undefined
}
-->
修改属性
对于一个已经存在的属性,它的默认描述符都为true。 因为与上文中的注意相悖,所以此处极容易混淆
let obj = {key: 123};
Object.getOwnPropertyDescriptor(obj, "key");
<!--
{
configurable: true,
enumerable: true,
writable: true,
value: 123
}
-->
// 可以修改
Object.defineProperty(obj, "key", {writable: false});
Object.getOwnPropertyDescriptor(obj, "key");
<!--
{
configurable: true,
enumerable: true,
writable: false,
value: 123
}
-->