最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 通过Object.defineProperty数据拦截对比,体验一下proxy有多优秀

    正文概述 掘金(时樾同学)   2020-12-17   508

    前言

    Object.defineProperty数据拦截和proxy数据拦截的对比

    这里通过vue1.x,vue2.x时的数据拦截来说一下Object.defineProperty

    Object.defineProperty

    先来用Object.defineProperty实现一下对象的拦截。

    
    let data = {
      m:234,
      n:[1,34,4,5676],
      h:{
        c:34
      }
    }
    
    function observer(data){
      if(typeof data === 'object'){
        Object.keys(data).forEach(key=>{
          defineReactive(data,key,data[key])
        })
      }
    }
    
    function defineReactive(obj,key,val){
      Object.defineProperty(obj,key,{
        get(){
          console.log('get')
          return val
        },
        set(newVal){
          console.log('set')
          if(newVal !== val ) val = newVal
        }
      })
    }
    
    

    上面通过遍历data的数据,进行了一次简单的拦截;看似没有问题,但如果我们改变data.h.c是不会触发set钩子的,为什么?因为这里还没有实现递归,所以只拦截了最表面的一层,里面的则没有被拦截。

    递归拦截对象

    
    function defineReactive(obj,key,val){
      observer(val)
      Object.defineProperty(obj,key,{
        get(){
          console.log('get')
          return val
        },
        set(newVal){
          console.log('set')
          if(newVal !== val ) val = newVal
        }
      })
    }
    
    

    递归拦截,只要在defineReactive函数再调一次observer函数把要拦截的值传给它就行。这样,就实现了对象的多层拦截。但是呢,现在是拦截不到数组的,当我们调用push,pop等方法它是不会触发set钩子的,为什么?因为Object.defineProperty压根就不支持数组的拦截。既然它不支持,那么我们只能拦截它的这些('push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse')改变自身数据的方法了。

    Object.defineProperty数组的拦截

    
    function arrayMethods(){
    
      const arrProto = Array.prototype
    
      const arrayMethods = Object.create(arrProto)
    
      const methods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse']
    
      methods.forEach(function (method) {
    
          const original = arrProto[method]
    
          Object.defineProperty(arrayMethods, method, {
    
              value: function v(...args) {
                  console.log('set arrayMethods')
                  return original.apply(this, args)
    
              }
    
          })
    
      })
      return arrayMethods
    }
    
    

    以上就是对这些数组的原型方法进行了一个拦截,然后把它覆盖要拦截的数组的原型就行,下面简单修改一下observer

    
    function observer(data){
      if(typeof data === 'object'){
        if(Array.isArray(data)){
          data.__proto__ = arrayMethods()
        }else{
          Object.keys(data).forEach(key=>{
            defineReactive(data,key,data[key])
          })
        }
      }
    }
    
    

    vue中,还会判断该key有没有__proto__,如果没有就直接把这些方法放到这个key的自身上,如果有就直接覆盖这个__proto__

    完整代码

    
    function arrayMethods(){
    
      const arrProto = Array.prototype
    
      const arrayMethods = Object.create(arrProto)
    
      const methods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse']
    
      methods.forEach(function (method) {
    
          const original = arrProto[method]
    
          Object.defineProperty(arrayMethods, method, {
    
              value: function v(...args) {
                  console.log('set arrayMethods')
                  return original.apply(this, args)
    
              }
    
          })
    
      })
      return arrayMethods
    }
    
    function observer(data){
      if(typeof data === 'object'){
        if(Array.isArray(data)){
          data.__proto__ = arrayMethods()
        }else{
          Object.keys(data).forEach(key=>{
            defineReactive(data,key,data[key])
          })
        }
      }
    }
    
    function defineReactive(obj,key,val){
      observer(val)
      Object.defineProperty(obj,key,{
        get(){
          console.log('get')
          return val
        },
        set(newVal){
          console.log('set')
          if(newVal !== val ) val = newVal
        }
      })
    }
    
    observer(data)
    
    

    以上,就完成了对对象和数组的拦截(说明:vue2.x中的实现会比这里复杂,但大概思路和大概实现是这样),看似辛苦点,换来一个完美的结果挺不错的。但真的是你想的那样吗?试一下调用data.n[1] = xxx,它是不会触发set钩子的,这也是在proxy出现之前,无能无力的,所以在vue中提供了$set,$deleteAPI。

    霸气的proxy登场

    这里就不介绍proxy了,就当你对它有了解过了。直接上代码

      let data = {
      m:234,
      n:[1,34,4,5676],
      h:{
        c:34
      }
    }
    
    function defineReactive(obj){
      Object.keys(obj).forEach((key) => {
        if(typeof obj[key] === 'object'){
          obj[key] = defineReactive(obj[key])
        }
      })
      return new Proxy(obj,{
        get(target,key){
          console.log('get')
          return target[key]
        },
        set(target,key,val){
          console.log('set')
          return target[key] = val
        }
      })
    }
    
     data = defineReactive(data)
    

    就这么一点代码就实现了对对象和数组的拦截(说明:这里不是vue3的实现方法,vue3怎么实现的,我还不知道,还没看过它的源码,有兴趣自己去看一下,然后顺便告诉我一下怎么实现的,哈哈哈)Object.defineProperty实现不了的,它能实现;Object.defineProperty实现的了,它也能实现。无论你调push,pop等方法它能拦截,你调data.n[1] = xxx也能拦截,简直不要太爽,这个两个版本的实现,给我个人的感觉就是一个韩红版(肉多腿短),一个迪丽热巴版(苗条腿长),哈哈哈,自己品。

    最后

    这里只是通过对对象和数组的拦截,来体验了一下proxy的威力;proxy能做的远远不止这样。

    • 了解proxy更多特性,可以看看我之前整理比较基础《Proxy入门》

    • 了解proxy更多用法,可以看看,前端小智的《Proxy 的巧用》

    如果你想了解更多proxy的特性,和更多的用例可以,看看以上两篇文章(自愿看哈,我不想给骂,说什么广告,瞎jb推荐之类的,?保命)。


    起源地下载网 » 通过Object.defineProperty数据拦截对比,体验一下proxy有多优秀

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元