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);
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='男'
控制台就会打印出写入的属性和属性值
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为字符串和整数看看,修改为字符串会抛出错误,修改整数才会被成功修改
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操作并且执行了删除操作
这是defineProperty
做不到的,除了delete以外还有很多其他的对象操作能被监视到:
apply(tagert,obj,args)
:拦截函数的调用、call
和apply
操作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上去实现就很麻烦
Proxy是以非侵入的方式监视对象的读写
一个定义好的对象,Proxy不需要对对象本身做任何操作就可以监视到对象内部成员的读写,而Object.defineProperty要求必须通过特定的方式单独去定义对象中需要被监视的属性,这需要去做很多额外的操作,可以看看上一篇的Vue.js 数据双向绑定的实现使用Object.defineProperty对对象属性的监视操作
本文正在参加「金石计划 . 瓜分6万现金大奖」
暂无评论内容