看Vue的双向绑定原理的时候,遇到了Object.defineProperty()
,虽然有过一点了解,但还不是很清晰,趁着不是很忙,就梳理了一下。
Object.defineProperty()
方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。
Object.defineProperty()
的语法是Object.defineProperty(obj,prop,descriptor)
,接收3个参数:
obj
:需要改变的对象prop
:需要改变的对象的属性descriptor
:将被定义或修改的属性描述符。
obj
与prop
就很好理解了,主要是descriptor
属性描述符不太好理解。
descriptor
descriptor
分为两种:并且必须是两种形式之一,不能同时存在。
- 数据描述符:具有值的属性,改值可能是可写的,也可能不是可写的。
- 存取描述符:有getter/setter函数对描述的属性。
两种属性的可选键值有:
configurable | enumerable | value | writable | get | set | |
---|---|---|---|---|---|---|
数据描述符 | yes | yes | yes | yes | no | no |
存取描述符 | yes | yes | no | no | yes | yes |
从上面的表格可以看出,如果有value或者writable就是数据描述符,如果有get/set就是存取描述符。
属性值的理解
configurable
:值为true时,该属性才能被改变,并且能被删除。默认为falseenumerable
:值为true时,属性才能够出现在对象的枚举属性中,并且新添加的属性才能显示在对象中。默认为falsevalue
:属性的值。默认为undefinedwritable
:值为true时,value才能被赋值运算符改变。默认为false。get
:一个属性提供getter的方法,如果没有getter则为undefined。当访问该属性是,该方法会被执行。默认为undefinedset
:一个给属性提供setter的方法,如果没有setter则为undefined。默认为undefined。
obj={a:1} 此时obj.a会有默认的{ value: 1, writable: true, enumerable: true, configurable: true }描述符,如果想要查看某个对象的属性有哪些描述符,可以使用Object.getOwnPropertyDescriptor(obj, prop)查看。 Object.getOwnPropertyDescriptor(obj, prop):该方法返回指定对象上一个自有属性对应的属性描述符。(自有属性指的是直接赋予该对象的属性,不需要从原型链上进行查找的属性)
enumerable
var obj = {};
Object.defineProperty(obj, "a", {
value: 1,
enumerable: false //false
});
console.log("obj.a", obj.a); //1
console.log("obj", obj); // {},此时a是不可枚举的,因此在获取obj的时候是没有办法看到a这个属性的
var obj = {};
Object.defineProperty(obj, "a", {
value: 1,
enumerable: true //true
});
console.log("obj.a", obj.a); //1
console.log("obj", obj); // {a:1}
⚠️不能对同一个对象的同一个属性使用两次Object.defineProperty(),否则会报错(当仅设置value并且value的值相同或者什么都不设置的时候除外)
//正确,如果是设置相同的属性并且属性值相同都正确,get/set除外
var obj = {};
Object.defineProperty(obj, "a", {
value: 1
});
Object.defineProperty(obj, "a", {
value: 1
});
//错误,如果设置相同属性不同属性值也会报错
Object.defineProperty(obj, "a", {
value: 1
});
Object.defineProperty(obj, "a", {
value: 2
});
//正确
Object.defineProperty(obj, "a", {});
Object.defineProperty(obj, "a", {});
//其他情况均报错
//Cannot redefine property: a
writable与configurable
writable为true的时候可以对属性使用赋值运算赋值,为false的时候不可以;而configurable为true的时候改属性才能修改并且被删除,为false的时候不可以。
这样看起来writable与configurable为true的时候都可以更改属性值,貌似作用一样,那又有什么区别呢,看例子:
1、writable:true,configurable:false
:可以更改值,但是不能删除
var obj = {};
Object.defineProperty(obj, "a", {
value: 1,
writable: true,
configurable: false
});
console.log(obj.a); // 1
obj.a = 2;
console.log(obj.a); // 2
delete obj.a;
console.log(obj.a); // 2
2、writable:true,configurable:true
:可以更改值,可以删除
var obj = {};
Object.defineProperty(obj, "a", {
value: 1,
writable: true,
configurable: true
});
console.log(obj.a); //1
obj.a = 2;
console.log(obj.a); //2
delete obj.a;
console.log(obj.a); //undefined
3、writable:false,configurable:true
:不可以更改值,可以删除
var obj = {};
Object.defineProperty(obj, "a", {
value: 1,
writable: false,
configurable: true
});
console.log(obj.a); //1
obj.a = 2;
console.log(obj.a); //1
delete obj.a;
console.log(obj.a); //undefined
4、writable:false,configurable:false
:不可以更改值,不可以删除
var obj = {};
Object.defineProperty(obj, "a", {
value: 1,
writable: false,
configurable: false
});
console.log(obj.a); //1
obj.a = 2;
console.log(obj.a); //1
delete obj.a;
console.log(obj.a); //1
由以上可以看出:writable
控制的是属性值是否可以更改,configurable
控制的是属性是否可以被删除
get/set
get
:取值
set
:设置值
var obj = {};
Object.defineProperty(obj, "a", {
// value: 1,
get: function() {
console.log("在这里获取值");
},
set: function() {
console.log("在这里设置值");
}
// configurable: false
});
obj.a = 2;
console.log(obj.a);
//打印结果是
在这里设置值
在这里获取值
2
⚠️get与set可以在自对象中被继承
function myclass() {
}
var value;
Object.defineProperty(myclass.prototype, "x", {
get() {
return value;
},
set(x) {
value = x;
}
});
var a = new myclass();
var b = new myclass();
a.x = 1;
console.log(b.x); // 1
如果不想被这样修改,可以使用this
来做
function myclass() {}
Object.defineProperty(myclass.prototype, "x", {
get() {
return this.stored_x; //随便定义的一个变量
},
set(x) {
this.stored_x = x;
}
});
var a = new myclass();
var b = new myclass();
a.x = 1;
console.log(a.x); // 1
console.log(b.x); // undefined
console.log(myclass.prototype.x); //undefined
不像访问者属性,值属性始终在对象自身上设置,而不是一个原型。然而,如果一个不可写的属性被继承,它仍然可以防止修改对象的属性。
function myclass() {
}
myclass.prototype.x = 1;
Object.defineProperty(myclass.prototype, "y", {
writable: false,
value: 1
});
var a = new myclass();
a.x = 2;
console.log(a.x); // 2
console.log(myclass.prototype.x); // 1
a.y = 2; //
console.log(a.y); // 1
console.log(myclass.prototype.y); // 1