Object.defineProperty()和 Proxy的区别

Proxy替代Object.defineProperty的原因在于Proxy能监听数组变化,支持更多拦截操作,且无需深度遍历,性能更优。尽管Proxy的兼容性较差,但在不考虑兼容性的场景下,Proxy是更好的响应式解决方案。
摘要由CSDN通过智能技术生成

众所周知,Object.defineProperty()和 Proxy的区别也是Vue2和Vue3响应式的区别,现在就聊一下为什么Proxy会替代Object.defineProperty()

Object.defineProperty():

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。

      const object1 = {}
      Object.defineProperty(object1, 'name', {
        value: 'taocheng',
        writable: false, //当 writable 属性设置为 false 时,该属性被称为“不可写的”。它不能被重新赋值
      })
      object1.property1 = 'zhangsan' // Throws an error in strict mode
      console.log(object1.name) //'taocheng'

语法:Object.defineProperty(obj, prop, descriptor)

参数:

obj:要定义属性的对象即代理的对象,

prop:要定义或修改的属性的名称或 Symbol 即代理对象的key,

descriptor:要定义或修改的属性描述符即配置项,get和set等都在这里配置。

使用Object.defineProperty()来监听对象属性的变化

  const object1 = {
        name: 'taocheng',
      }
      let recordName = 'ljr'
      Object.defineProperty(object1, 'name', {
        enumerable: true,//enumerable 定义了对象的属性是否可以在 for...in 循环和 Object.keys() 中被枚举。
        configurable: true,//configurable 特性表示对象的属性是否可以被删除
        set(newVal) {
          recordName = newVal
          console.log('set', recordName) //set ljr
        },
        get() {
          console.log('get', recordName) //get ljr
          return recordName
        },
      })
      object1.name = recordName
      console.log(object1.name) //ljr

对一个对象进行整体的响应式监听:

 // 监视对象
      function observe(obj) {
        // 遍历对象,使用 get/set 重新定义对象的每个属性值
        Object.keys(obj).map(key => {
          defineReactive(obj, key, obj[key])
        })
      }

      function defineReactive(obj, k, v) {
        // 递归子属性
        if (typeof v === 'object') observe(v)

        // 重定义 get/set
        Object.defineProperty(obj, k, {
          enumerable: true,
          configurable: true,
          get: function reactiveGetter() {
            console.log('get: ' + v)
            return v
          },
          // 重新设置值时,触发收集器的通知机制
          set: function reactiveSetter(newV) {
            console.log('set: ' + newV)
            v = newV
          },
        })
      }

      let person = {
        name: 'taocheng',
        other: {
          age: 22,
        },
      }
      // 监视对象
      observe(person)
      person.name // get: taocheng
      person.name = 'ljr' // set: ljr
      person.name //get: ljr
      //以下会输出两次结果
      person.other.age //get: [object Object] get: 22
      person.other.age = 33 // get: [object Object] set: 33
      person.other.age //get: [object Object] get: 33

如上,遇到子对象就会递归调用defineReactive方法

缺点:

(1)object.defineproperty 无法监控到数组下标的变化,导致通过数组下标添加元素,无法实时响应

(2)object.defineProperty 只能劫持对象的属性,从而需要对每个对象,每个属性进行遍历,如果,属性值是对象,还需要深度遍历。

(3)无法检测到对象属性的新增或删除

Proxy:

Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。

语法:const p = new Proxy(target, handler)

参数:

target:要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。

handler:一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。

例子1:

      const handler = {
        get: function (target, name) {
          return name in target ? target[name] : 'no prop!'
        },
        set: function (target, prop, value, receiver) {
          target[prop] = value
          console.log('property set: ' + prop + ' = ' + value)
          return true
        },
      }

      var person = new Proxy({}, handler)
      person.name = 'taocheng' // property set: name = taocheng
      console.log(person.name) // taocheng
      console.log(person.age) // no prop!

例子2:

  const handler = {
        get: function (target, name) {
          return name in target ? target[name] : 'no prop!'
        },
        set: function (target, prop, value, receiver) {
          target[prop] = value
          console.log('property set: ' + prop + ' = ' + value)
          return true
        },
      }
      const person = { name: 'taocheng', age: 25 }
      var newPerson = new Proxy(person, handler)
      newPerson.salary = 500 // property set: salary = 500
      console.log(person.name) // taocheng
      console.log(person.age) // 25
      console.log(person.salary) //500
      // 此时分别打印person和newPerson,如下图
      console.log(person)
      console.log(newPerson)

发现newPerson是经过Proxy代理之后的 

并且haldler对象有以下的方法

详情前往mdn:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy

总结:

1.proxy有多达13种拦截方法,不限于apply、ownKeys、deleteProperty、has等是object.defineProperty不具备的。
2.proxy可以直接监听数组的变化,proxy可以直接监听对象而非属性。
vue2.0中,比如vm.arr.length–或者vm.arr[0]=100均不能触发视图更新;
3.proxy返回的是一个新对象,我们可以只操作新对象达到目的,不需要深度遍历监听,性能高于Object.defineProperty;而Object.defineProperty只能遍历对象属性直接修改。

Proxy除了兼容性不足以外,其他方面的表示都优于Object.defineProperty

所以如果不考虑兼容性,推荐使用Proxy 监测变量变化

参考文档:

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/definePropertyhttps://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy

https://juejin.cn/post/7218861498592411704

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

多看书少吃饭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值