ES6中的Proxy


theme: smartblue
highlight: agate

前言

如果我们想要监视对象中的属性读写,可以使用es5提供的Object.defineProperty()为对象添加属性,就可以捕获到对象中的属性读写的过程,这种方法应用非常广泛,在vue3.0之前的版本就是使用Object.defineProperty()实现数据响应来完成数据的双向绑定

在es2016中全新设计了一个叫Proxy的类型,Proxy 这个词的原意是代理,用在这里表示由它来”代理”某些操作,可以译为”代理器”,Proxy就是专门为对象设置访问代理器的,无论是读还是写都要经过代理,通过Proxy就能轻松监视对象的读写过程

Proxy的使用

如何使用Proxy监视对象的读写过程呢?定义一个person对象,对象当中有一个name属性和height属性,然后通过new Proxy的方式为person创建一个代理对象

Proxy的构造函数需要2个参数,一个是需要代理的目标对象,另一个是代理的处理对象,在这个处理对象中可以通过get()方法监视对象属性的访问,通过set()方法监视对象设置属性的过程

const person={
    name:'zzz',
    height:185
}
const proxy=new Proxy(person,{
    get(){//监视对象属性的访问

    },
    set(){//监视对象设置属性的过程

    }
})

get()方法

get()方法可以接收两个参数,第一个是代理的目标对象,第二个是访问的属性名,分别把它们打印出来

const proxy=new Proxy(person,{
    get(target,property){// 目标对象  访问的属性名
        console.log(target,property);
    },
    set(){

    }
})
console.log(proxy.name);

image.png

get()方法正常的逻辑应该是判断代理目标对象中是否存在访问的属性名,存在就返回对应的值,不存在就返回undefined或者一个默认值

get(target,property){
    return property in target? target[property]:'default'
},

//分别打印存在的属性和不存在的属性
console.log(proxy.name); //zzz
console.log(proxy.age); //default

set()方法

set()方法接收三个参数,第一个是代理的目标对象,第二个是要写入的属性名,第三个是要写入的属性值,分别在方法里将这三个参数打印出来并在外边添加一个属性

set(target,property,value){
    console.log(target,property,value);
}

proxy.sex='男'

控制台就会打印出写入的属性和属性值

image.png

set()方法正常的逻辑应该是为代理目标设置指定属性,在设置之前先做一些数据校验,例如属性名为height,那么那么就要判断它的是否是一个数字,不是就抛出错误

set(target,property,value){
    if(property === 'height'){ //判断属性名是否为height
        if(!Number.isInteger(value)){//判断是否为整数
            throw new TypeError(`${value} is not an int`)
        }
    }
    target[property]=value
}

我们分别在控制台去修改height为字符串和整数看看,修改为字符串会抛出错误,修改整数才会被成功修改

7.gif

Proxy对比Object.defineProperty

相比于Object.defineProperty,Proxy更为强大

Proxy能够监视到更多的对象操作

defineProperty只能监视属性的读写,而Proxy可以监视到更多的对象操作,例如delete操作,对对象当中方法的调用……

在Proxy对象的处理对象当中添加一个deleteProperty()代理方法,这个方法会在外部在对代理对象进行操作的时候自动执行

这个方法接收两个参数,代理目标对象和所要删除的属性名称,在这方法内部打印一下要删除的属性名称,然后正常执行delete操作,之后在外部去进行delete操作

deleteProperty(target,property){
    console.log('delete',property);
    delete target[property]
},

//外部执行删除操作
delete proxy.name
console.log(proxy);

通过控制台可以看成功拦截了delete操作并且执行了删除操作

image.png

这是defineProperty做不到的,除了delete以外还有很多其他的对象操作能被监视到:

  • apply(tagert,obj,args):拦截函数的调用、callapply操作
  • has(tagert,key):判断对象是否具有某个属性
  • construct(tagert,args):拦截new命令
  • deleteProperty(tagert,key):拦截delete操作
  • defineProperty(tagert,key,desc):拦截Object.defineProperty()操作
  • getOwnPropertyDescriptor(tagert,key):拦截Object.getOwnPropertyDescriptor()
  • getPrototypeOf(tagert):拦截获取对象原型
  • isExtensible(tagert):拦截Object.isExtensible()操作
  • ownKeys(tagert):拦截对象自身属性的读取操作
  • preventExtensions(tagert):拦截Object.preventExtensions()
  • setPrototypeOf(tagert,proto):拦截Object.setPrototypeOf()方法

Proxy能更好的支持数组对象的监视

以往通过Object.defineProperty监视数组的操作最常见的就是通过重写数组的操作方法了,在vue.js里也是使用这种方式通过自定义方法覆盖掉数组原型对象上的push(),shift()等方法,以此来劫持对应方法调用的过程

那我们如何使用Proxy对象来监视数组?先定义一个数组,为这个数组定义一个Proxy对象,在这个Proxy对象的处理对象上添加set()方法监视数据的写入,将写入的值赋值给目标对象对应的属性名,然后返回一个true代表写入成功

const list=[]
const proxy=new Proxy(list,{
    set(target,property,value){
        console.log(target,property,value);
        target[property]=value
        return true //代表写入成功
    }
})

现在在外部对数组的写入操作都会被监视到,通过这个proxy对象的push方法往数组中添加数据,就可以看到数据被写入了并且控制台成功打印了,这就是proxy对数组的监视,要是放在Object.defineProperty上去实现就很麻烦

2.gif

Proxy是以非侵入的方式监视对象的读写

一个定义好的对象,Proxy不需要对对象本身做任何操作就可以监视到对象内部成员的读写,而Object.defineProperty要求必须通过特定的方式单独去定义对象中需要被监视的属性,这需要去做很多额外的操作,可以看看上一篇的Vue.js 数据双向绑定的实现使用Object.defineProperty对对象属性的监视操作

本文正在参加「金石计划 . 瓜分6万现金大奖」

© 版权声明
THE END
喜欢就支持一下吧
点赞6 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容