从Vue认识了Object.defineProperty()方法,觉得这个方法挺神奇,在这里做一个总结。参考来源是《JavaScriot高级程序设计》这本书。
Object.defineProperty()的用途
1.修改对象中的某个属性的特性
特性描述了属性的各种特征,在JavaScript中不能直接访问。
- 参数:
(1)object:修改的对象。
(2) property:修改的属性名。
(3)描述符对象,该对象包含以下属性(这些属性都是object中的某属性的特性):
configurable:能否修改属性配置,能否删除属性等。注意,只要修改为false,则不能再改变为true了。
enumerable:能否通过遍历返回属性。
var person = {
name:'Alice',
_age:10,//前面的下划线是一种常用的记号,用于表示只能通过对象方法访问的属性
isAdult: false
}
Object.defineProperty(person,"sex",{
value:'female',
enumerable:true//可枚举
})
console.log(Object.keys(person));//[ 'name', '_age', 'isAdult', 'sex' ]
Object.defineProperty(person,"sex",{
enumerable:false//不可枚举
})
console.log(Object.keys(person));//[ 'name', '_age', 'isAdult' ]
writable:属性是否可修改。
value:属性的数据值。
var person = {
name:'Alice',
age:18
}
Object.defineProperty(person,"name",{
writable:false//给person的name属性设置为只读
})
person.name = 'Bob';//修改name属性值
console.log(person.name);//Alice, 说明修改未成功,在严格模式下会报错
- 可以通过Object.getOwnPropertyDescriptor()来访问某个对象中某个属性的特性。
var descriptor = Object.getOwnPropertyDescriptor(obj,'title');
console.log(descriptor.configurable);//true
console.log(descriptor.value);//Alice
2.创建一个新属性
如果调用该方法创建一个新属性时,如果不指定,value默认为undefined,其他特性都默认为false。
var obj = {};
Object.defineProperty(obj,'title',{
configurable: true,
writable: true,
value: 'Alice'
})
console.log(obj.title);//Alice, 给obj定义了一个title属性,值为Alice
var descriptor = Object.getOwnPropertyDescriptor(obj,'title');
console.log(descriptor.configurable);//true
console.log(descriptor.value);//Alice
3.定义访问器属性(数据响应式原理)
什么是访问器属性?
访问器属性不包含数据值,它们包含一对setter和getter函数。在读取访问器属性时,会调用getter函数,这个函数负责返回有效的值;在写入访问器属性时,会调用setter函数并传入新值,这个函数负责决定如何处理数据。
该方法可以做到:设置一个属性的值会导致其他属性发生变化(响应式原理)
var person = {
name:'Alice',
_age:10,//前面的下划线是一种常用的记号,用于表示只能通过对象方法访问的属性
isAdult: false
}
Object.defineProperty(person,'age',{
get: function(){
return this._age;
},
set: function(newAge){
this._age = newAge;
if(newAge >= 18){
this.isAdult = true;
}
else{
this.isAdule = false;
}
}
})
console.log(person);//{ name: 'Alice', _age: 10, isAdult: false }
console.log(person.age);//10,age属性在访问时,通过get函数进行访问
person.age = 18;//修改通过Object.defineProperty定义的age属性,该属性会通过set函数进行设置
console.log(person);//{ name: 'Alice', _age: 18, isAdult: true }
扩展:Object.defineProperties()可以定义多个属性及其特性
Object.defineProperty()方法的缺点
这里说的是用该方法实现数据响应式框架的缺点。
- 无法检测到对象属性的新增或删除
由于js的动态性,可以为对象追加新的属性或者删除其中某个属性,这点对经过Object.defineProperty方法建立的响应式对象来说,只能追踪对象已有数据是否被修改,无法追踪新增属性和删除属性,这就需要另外处理。
解决方法: 如果需要扩展对象,必须手动给新属性设置setter和getter方法。
Vue中的解决方法:
不在data中预先声明好的属性无法进行双向绑定,需要通过this.$set()来设置。
this.$set(this.data.key, value) //1.0写法
this.$set(this.data,"key",value)//2.0写法
-
通过遍历对象的属性进行数据劫持,但是属性值也是对象就需要深度遍历了,性能不好。
-
通过下标方式修改数组数据或者给对象新增属性并不会触发组件的重新渲染,因为Object.defineProperty不能拦截到这些操作,更精确的来说,对于数组而言,大部分操作都是拦截不到的。
解决方法: 通过原型继承得到一个新的原型对象,在此基础上,劫持了7种常用的数组操作进行了重写,分别是push() 、pop() 、shift()、 unshift() 、splice() 、sort()、 reverse()。
参考资料:
扩展:Proxy和Object.defineProperty()
- Proxy可以劫持整个对象,并返回一个新的对象。defineProperty只能劫持对象的属性,如果对象的属性也是对象,就要进行深度遍历。
- Proxy不仅可以代理对象,还可以代理数组。还可以代理动态增加的属性。
Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”(meta programming),即对编程语言进行编程。Proxy 可以理解成,在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写,可以这样认为,Proxy是Object.defineProperty的全方位加强版。
https://blog.csdn.net/qq_24510455/article/details/100077701
其他参考资料: