ES2015 Proxy 对比 defineProperty 简单对比

134 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

本文同时参与 「掘力星计划」        ,赢取创作大礼包,挑战创作激励金

前言

作为一名前端开发工程师,Vue大家应该都很熟悉了吧!对于Vue的响应式实现肯定或多或少都知道一些:

  • Vue2: Object.defineProperty
  • Vue3: Proxy 想必大家都知道,可是为什么这次的版本升级需要把Object.defineProperty切换为Proxy呢?

带着这个问题,我们接下来进行一些评估就知道了

Object.defineProperty

Vue中是通过设定对象属性的 setter/getter 方法来监听数据的变化,通过getter进行依赖收集,而每个setter方法就是一个观察者,在数据变更的时候通知订阅者更新视图。

当然这样也出现了几个问题:

  • 问题1:Object.defineProperty只能监听到属性的读取或者写入 案例:
let data = {
  value: ''
}

测试:

Object.defineProperty(data,'value',{
    configurable: true,
    enumerable: true,
    get: function () {
        console.log('get')
        return '-'
    },
    set: function (n) {
      console.log('set',n)
    }
})

data.key = 'key'
data.value = 'value'
console.log(data)

测试结果: image.png 我们重新设置的key的时候并没有触发set方法,对value这个已经存在的属性却触发了set方法

Vue中如何解决的:

vm.$set( target, propertyName/index, value )
  • 问题2:对象属性的删除无法被发现
let data = {
  data1: '-'
}
Object.defineProperty(data,'data1',{
	configurable: true,
    enumerable: true,
    get: function () {
        console.log('get')
        return '-'
    },
    set: function (n) {
      console.log('set',n)
    }
})
delete data.data1
console.log(data)

我们删除了属性,却没有任何触发事件

测试结果: image.png

Vue中如何解决的:

vm.$delete( target, propertyName/index )
  • 问题3: 数组的变动 这里我们就不进行代码的演示了,熟悉Vue的同学都知道,Object.defineProperty对数组的的操作非常的差.... Vue中可以对数组进行监听主要是因为他对数组的操作方法进行了重写也就是变异方法,遇到数组的时候,我们就需要进行一次遍历对每一项进行依赖的添加。

Proxy

Proxy是对对象属性进行的劫持

  • 解决1:能监听到属性的读取或者写入
let data = {
  value: ''
}
data = new Proxy(data, {
  get (obj, prop) {
    if (prop === 'key') {
      return 'key'
    }
    return '-';
  },
  set (obj, prop, value) {
    console.log(value)
    obj[prop] = value
  }
})
data.key = 'key'
data.value = 'value'
console.log(data)

测试结果: image.png

我们重新设置的key的时候触发了set方法,对value这个已经存在的属性也触发了set方法

  • 解决2:
let data = {
  data1: ''
}

let validator = {
 deleteProperty(targget, property) {
   console.log('delete', property)
   delete targget[property]
 }
};
data = new Proxy(data, validator);
delete data.data1
console.log(data)

测试结果: image.png 可以看出,我们删除的时候也可以触发到删除的情况,那这样我们就能在删除动作的时候做到通知视图层了

  • 解决3:
let data = [1,2,3,4,5,6]
data = new Proxy(data, {
  get (obj, prop) {
    return obj[prop];
  },
  set (obj, prop, value) {
    console.log('set',prop, value)
    obj[prop] = value
    return true
  }
})
data.push(7)
data.push(8)
data.slice(1,1)
console.log(data)

操作结果: image.png

Proxy缺点

不幸的是,Proxy有一个致命缺陷:性能。

更打击人的是,Object.observe()就是因为性能被废弃的,而且Proxy 不兼容IE,也没有 polyfill,但是Proxy 作为新标准将受到浏览器厂商重点持续的性能优化,希望之后会做的更好吧! 有一个文章写的很好,推荐一下ES6 Proxy 性能之我见