javaScript es5 提供了Object.defineProperty方法,允许对对象的getter/settet进行拦截,该方法会直接在一个对象上定义一个新属性,或者修改现有属性,并返回此对象。
我们可以理解为是针对对象上的某一个属性做处理的。
语法: Object.defineProperty(obj, prop, descriptor)
* obj 要定义属性的对象
* prop 要定义或修改的属性的名称或Symbol
* descriptor 要定义或修改的属性描述符
const obj = {}
Object.defineProperty(obj, "a", {
value: 1,
writable: false, // 是否可写
configurable: false // 是否可配置
})
vue3之前双向绑定都是由defineProperty来实现,3.0之后重构为proxy,他们的区别在哪里呢?
const obj = {};
Object.defineProperty(obj, 'a', {
set(val) {
console.log(`开始设置新值: ${val}`)
},
get() {
console.log(`开始读取属性`)
return 1;
},
writable : true
})
obj.a = 2 // 开始设置新值: 2
obj.a // 开始获取属性
在vue中,当我们使用或者修改对象时,对应的get和set就会被执行,然后就会触发watch中关于这条数据的监听函数,从而更新视图
但是对象新增属性的时候视图时不更新的
因为data init 是在生命周期created之前的操作,会对data绑定一个观察者Observer,之后data中的字段更新都会通知依赖收集器 Dep 触发视图更新
回到defineProperty 本身,是对对象上的属性做操作,而非对象本身
在Observer data时,新增的属性并不存在,自然就不会有getter和setter,所以视图也就不更新
vue提供的全局$set 本质上也是给新增的属性手动observer
由于js限制,vue不能检测数组通过索引设置元素
虽然defineProperty支持通过索引修改数组项,但是尤大考虑到性能因素,摒弃了。
但是defineProperty做不到新增索引,所以就有了数组的变异方法
对比
-- proxy 作为新标准将受到浏览器厂商重点持续的性能优化
-- proxy 能观察的类型比defineProperty更丰富
-- proxy 不兼容ie,也没有polyfill,defineProperty 能支持到ie9
-- Object.defineProperty 是劫持对象的属性,新增元素需要再次defineProperty.而proxy劫持的是整个对象,不需要做特殊处理
-- 使用defineProperty时,我们修改原来的obj对象就可以触发拦截,而使用proxy,就必须修改代理对象,即proxy的实例