Proxy 是 ES6 为了操作对象引入的 API 。
Proxy 可以对目标对象的读取、函数调用等操作进行拦截,然后进行操作处理。它不直接操作对象,而是像代理模式,通过对象的代理对象进行操作,在进行这些操作时,可以添加一些需要的额外操作。
1.简单用法
let star = {
name: '我是小阳仔',
age: 18
}
let proxy = new Proxy(star,{
get(targetObj, propoty, receiver) {
console.log(`我是被代理的对象${targetObj}`)
console.log(`我是你访问的被代理的属性${propoty}`)
//receiver是代理对象proxy
return targetObj[propoty]
}
})
console.log(proxy.name)
这就是一个最简单的proxy的例子。 首先我们需要利用Proxy构造函数创建一个Proxy对象,我们需要向这个构造函数中传递两个参数,第一个参数是被代理的对象,第二个参数是一个捕获器对象。我们需要代理的所有行为都是在捕获器对象中定义的。
注意:只有当我们访问代理的时候才会被捕获,这时候我们仍然可以访问原来的对象,和设置捕获钱没有任何的变化。
2.Proxy代理另一个代理
Proxy是可以设置多层代理的,即代理之上还有代理。具体实现:
let proxyFirst = new Proxy(obj,{})
let proxySecond = new Proxy(proxyFirst,{})
我们通过proxySecond访问obj对象 的时候,不仅会触发他本身在捕获器对象中定义的捕获方法,还会触发proxyFirst 的。
我们通过proxyFirst 访问obj对象的时候,那么只会触发定义的在proxyFirst 身上的捕获方法。
3.捕获器中可以使用的方法
get()、set()、has、defineProperty()、getOwnPropertyDescriptor()、deleteProperty() 、ownKeys()、getPrototypeOf()、setPrototypeOf()、isExtensible()、preventExtensions()、construct()、apply()
捕获器有13种,这里我们重点介绍4种,即对象的增删改查。由于我们已经介绍过get()下面我们将介绍常用的3个。
· set()
set即设置,就是我们在设置对象属性的时候会捕获到。示例:
let proxy = new Proxy(star,{
set(targetObj, property, value, receiver) {
if(property == 'height') {
if(typeof value === 'number') {
return Reflect.set(targetObj, property, `${value+10}cm`)
}else {
throw '身高只能是数字类型的值'
}
}
}
})
proxy.height = 180
console.log(proxy.height)
在这个简单的示例当中,我们给star对象设置了一个代理,这个代理就是捕获在给star设置身高的时候,多加10cm。set()方法接收三个参数(目标对象,设置的属性,设置的值,proxy对象)
· has()
has()即在查找的时候会被捕获,for…in ,has()代码如下:
let proxy = new Proxy(star,{
has(targetobj, property) {
return Reflect.has(...arguments)
}
})
proxy.height = 180
console.log('fansNums' in proxy)
has()捕获器接收两个参数,第一个参数是目标对象,第二个参数是引用的目标对象上字符串的属性。利用has捕获器,我们可以把不想让别人访问到的对象的属性隐藏掉。
· deleteProperty()
在我们删除属性的时候detete()捕获器会被触发。直接上代码:
let proxy = new Proxy(star,{
deleteProperty(targetobj, property) {
if(property === 'name') {
return false
}else {
return Reflect.deleteProperty(...arguments)
}
}
})
delete proxy.name
console.log(proxy.name)
4.创建可撤销的代理
const {proxy, revoke} = Proxy.revocable(star,{
get(targetObj, propoty, receiver) {
console.log(`我是被代理的对象${targetObj}`)
console.log(`我是你访问的被代理的属性${propoty}`)
//receiver是代理对象proxy
return targetObj[propoty]
}
})
// revoke()
console.log(proxy.name)
这和普通的创建proxy实例的方式有些区别,使用的是Proxy.revocable创建的代理。当我们不想使用此代理的时候可以使用revoke()直接将其删除即可。
5.Proxy使用场景:实现数据绑定
众所周知,VUE3之前,双向绑定是使用Object.defineProperty,而VUE3则是基于proxy来实现的。为什么呢?肯定是为了改进一些缺陷啊!
Object.defineProperty 不足
无法监听数组的变化: 数组的这些方法是无法触发set的:push, pop, shift, unshift,splice, sort, reverse.,vue中能监听是因为对这些方法进行了重写
只能监听属性,而不是监听对象本身,需要对对象的每个属性进行遍历。对于原本不在对象中的属性难以监听。
实现
可以很方便的使用 Proxy 来实现一个数据绑定和监听。
let onWatch = (obj, setBind, getLogger) => {
let handler = {
get(target, property, receiver) {
getLogger(target, property)
return Reflect.get(target, property, receiver);
},
set(target, property, value, receiver) {
setBind(value);
return Reflect.set(target, property, value);
}
};
return new Proxy(obj, handler);
};
let dataObj = { a: 1 }
let value
let p = onWatch(dataObj, (v) => {
value = v
}, (target, property) => {
console.log(`Get '${property}' = ${target[property]}`);
})
p.a = 2 // bind `value` to `2`
p.a // -> Get 'a' = 2
欢迎在评论区交流。
如果文章对你有所帮助,❤️关注+点赞❤️鼓励一下!博主会持续更新。。。。
我的博客原文:ES6 proxy 看这一篇就够啦!