双向绑定Proxy VS Object.defineProperty


Vue3.0的双向绑定将使用Proxy代替Object.defineProperty,据尤大说,速度提升了1倍。

本文我们来探讨一下Proxy对比Object.defineProperty究竟有哪些优劣呢?

 

首先介绍一下什么是Proxy?

Proxy在ES6规范中被正式发布,Proxy可以理解成在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。

Proxy语法:

ES6原生提供Proxy构造函数,用来生成Proxy实例

var proxy = new Proxy(target,handler);

Proxy接受两个参数:

target:要代理目标对象

handler: 处理函数,该函数将拦截对应的操作

下面是 Proxy 支持的拦截操作一览,一共 13 种。

  • get(target, propKey, receiver):拦截对象属性的读取,比如proxy.fooproxy['foo']
  • set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = vproxy['foo'] = v,返回一个布尔值。
  • has(target, propKey):拦截propKey in proxy的操作,返回一个布尔值。
  • deleteProperty(target, propKey):拦截delete proxy[propKey]的操作,返回一个布尔值。
  • ownKeys(target):拦截Object.getOwnPropertyNames(proxy)Object.getOwnPropertySymbols(proxy)Object.keys(proxy)for...in循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
  • getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
  • defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc)Object.defineProperties(proxy, propDescs),返回一个布尔值。
  • preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值。
  • getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象。
  • isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值。
  • setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
  • apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)proxy.call(object, ...args)proxy.apply(...)
  • construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)

下面介绍两个常用的拦截方法:get方法和set方法。

//get方法用于拦截某个属性的读取操作
//接受三个参数,依次为目标对象、属性名和Proxy实例本身,最后一个参数可选

//set方法用于拦截某个属性的赋值操作
//接受四个参数,一次为目标对象、属性名、属性值和Proxy实例本身,最后一个参数可选

var person = {
  name:'Jack',
  age:20
};
var handler = {
  get(target,key){
    if(key in target){
      console.log(`${key}被读取`);
      return target[key]
    }else{
      throw new ReferenceError(`Property ${key} does not exist`)
    }
  },
  set(target,key,value){
    console.log(`${key}被设置为${value}`)
    target[key] = value
  }
};
let instance = new Proxy(person,handler);
instance.name  //name被读取
instance.age = 25    //age被设置为25

 

Object.defineProperty缺点:

1. 对数组的支持不好,无法监听到数组的变化,在Vue官方文档说明了可以监听到数组的变动,但只限于push、pop、shift、unshift、splice、sort、reverse方法。实际上是他们对这几种方法进行了重写。

var arrayProto = Array.prototype;
var arrayMethods = Object.create(arrayProto);
[
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
].forEach(function(item){
    Object.defineProperty(arrayMethods,item,{
        value:function mutator(){
            //缓存原生方法,之后调用
              console.log('array被访问');
          var original = arrayProto[item]    
          var args = Array.from(arguments)
         original.apply(this,args)
            // console.log(this);
        },
    })
})

2.Object.defineProperty监听的是对象的属性,当一个对象为深层嵌套的时候,必须进行递归遍历,比较麻烦。

  

Proxy对比Object.defineProperty:

优点:

  1. Proxy可以劫持整个对象,这样以来操作便利程度远远优于Object.defineProperty。

  2. Proxy可以直接监听数组的变化,无需进行数组方法重写。

var arr = [1,2,3,4];
  var instance = new Proxy(arr,{
      get(target,key){
         console.log('数组被读取')
         return Reflect.get(target,key)
        },
        set(target,key,val){
          console.log('监听到数组更新')
          return Reflect.set(target,key,val)
        }
   })

instance[0] = 5   //监听到数组更新
instance.push(6)  //数组被读取  监听到数组更新

  3. Proxy支持13种拦截操作,是Object.defineProperty不具备的。

 

缺点:Proxy的兼容性不是太好,不兼容IE,且无法通过polyfill提供兼容。 

 

 

 

 

 

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM