一、前言
Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象;
该方法也是vue实现数据绑定和更新数据的核心代码之一。
二、语法
Object.defineProperty(obj,prop,descriptor)
- obj:要定义属性的对象;
- prop:要定义或修改的属性名称;
- descriptor:要定义或修改的属性描述符;
三、属性描述符
对象的属性描述符也是一个普通对象,主要有两种形式:数据描述符和存取描述符;
- 数据描述符:具有值的属性和值是否可写的属性;
- 存取描述符:具有 getter 函数和 setter 函数所描述的属性;
一个描述符只能是这两者其中之一;不能同时是两者
这两种描述符对象,共享以下两个键值(默认值是指在使用 Object.defineProperty() 定义属性时的默认值):
- configurable
当且仅当该键值为 true 时,该属性的描述符才能够被改变(即可以被重新定义),同时该属性也能从对应的对象上被删除。
默认值:false。
例如:
var obj = {};
Object.defineProperty(obj,'fullName1',{
configurable:false,
value:1
})
Object.defineProperty(obj,'fullName2',{
configurable:true,
value:2
})
console.log(obj); // {fullName1: 1, fullName2: 2}
delete obj.fullName1;
delete obj.fullName2;
console.log(obj); // {fullName1: 1} fullName1属性没有被删除,fullName2属性被删除了
- enumerable
当且仅当该键值为 true 时,该属性才会出现在对象的枚举属性中。
默认值:false。
例如:
var obj = {};
Object.defineProperty(obj,'fullName1',{
configurable:true,
value:1,
enumerable:false
})
Object.defineProperty(obj,'fullName2',{
configurable:true,
value:2,
enumerable:true
})
console.log(Object.keys(obj)) // ['fullName2']
数据描述符具有以下两个特有可选键值:
- value
该属性对应的值。
默认值:undefined。 - writable
当且仅当该键值为 true 时,属性的值,也就是上面的 value,才能被改变。
默认值:false。
例如:
var obj = {};
Object.defineProperty(obj,'fullName1',{
configurable:true,
value:1,
enumerable:false,
writable:false
})
Object.defineProperty(obj,'fullName2',{
configurable:true,
value:2,
enumerable:true,
writable:true
})
obj.fullName1 = 3;
obj.fullName2 = 4;
console.log(obj) // {fullName2: 4, fullName1: 1}
// 只有fullName2被修改了
存取描述符具有以下两个特有可选键值:
- get
属性的 getter 函数,如果没有 getter,则为 undefined。**当访问该属性时,会调用此函数。**执行时不传入任何参数,但是会传入 this 对象(由于继承关系,这里的this并不一定是定义该属性的对象)。该函数的返回值会被用作属性的值。
默认值:undefined。
例如:
var obj = {};
Object.defineProperty(obj,'firstName',{
configurable:true,
enumerable:true,
value:'zhang'
})
Object.defineProperty(obj,'endName',{
configurable:true,
enumerable:true,
value:'san'
})
Object.defineProperty(obj,'fullName',{
configurable:true,
enumerable:true,
get(){
return this.endName+'.' +this.firstName;
}
})
console.log(obj.fullName); // san.zhang 当属性被访问时,才会调用函数
-
set
属性的 setter 函数,如果没有 setter,则为 undefined。**当属性值被修改时,会调用此函数。**该方法接受一个参数(也就是被赋予的新值),会传入赋值时的 this 对象。
默认值:undefined。
例如:var obj = {}; Object.defineProperty(obj,'firstName',{ configurable:true, enumerable:true, value:'zhang', writable:true, }) Object.defineProperty(obj,'endName',{ configurable:true, enumerable:true, value:'san', writable:true }) Object.defineProperty(obj,'fullName',{ configurable:true, enumerable:true, get(){ return this.endName+'.' +this.firstName; }, set(value){ let temp = value.split('.'); console.log(temp) this.endName = temp[0]; this.firstName = temp[1]; } }) console.log('firstName:'+obj.firstName,'endName:'+obj.endName,'fullName:'+obj.fullName); // firstName:zhang endName:san fullName:san.zhang obj.fullName = 'si.li'; console.log('firstName:'+obj.firstName,'endName:'+obj.endName,'fullName:'+obj.fullName); // firstName:li endName:si fullName:si.li
“如果一个描述符不具有 value、writable、get 和 set 中的任意一个键,那么它将被认为是一个数据描述符。如果一个描述符同时拥有 value 或 writable 和 get 或 set 键,则会产生一个异常”
注意:
这些选项不一定是自身属性,也要考虑继承来的属性。为了确保在设置前,保留这些默认值,可以明确指定所有的选项值,或者通过Object.create(null) 将 __ proto__ 指向null。
例如:
// 使用 __proto__
var obj = {};
var descriptor = Object.create(null); // 没有继承的属性
// 默认没有 enumerable,没有 configurable,没有 writable
descriptor.value = 'static';
Object.defineProperty(obj, 'key', descriptor);
// 显式
Object.defineProperty(obj, "key", {
enumerable: false,
configurable: false,
writable: false,
value: "static"
});