Proxy的定义
Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
Proxy的语法
const p = new Proxy(target, handler)
- target: 要代理的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)
- handler: 是一个对象,我们可以把这个对象称之为代理的处理对象,属性值为各个处理方法,用来对代理对象进行拦截处理
handler 对象的方法
-
getPrototypeOf(): Object.getPrototypeOf 方法的捕捉器。
-
setPrototypeOf(): Object.setPrototypeOf 方法的捕捉器。
-
isExtensible(): Object.isExtensible 方法的捕捉器。
-
preventExtensions(): Object.preventExtensions 方法的捕捉器。
-
getOwnPropertyDescriptor(): Object.getOwnPropertyDescriptor 方法的捕捉器。
-
defineProperty(): Object.defineProperty 方法的捕捉器。
-
has(): in 操作符的捕捉器。
-
get(): 属性读取操作的捕捉器。
-
set(): 属性设置操作的捕捉器。
-
deleteProperty(): delete 操作符的捕捉器。
-
ownKeys(): Object.getOwnPropertyNames 方法和 Object.getOwnPropertySymbols 方法的捕捉器。
-
apply(): 函数调用操作的捕捉器。
-
construct(): new 操作符的捕捉器。
proxy实例
读取对象的属性值操作
let obj = {
name: 'mike',
like: ['football', 'baskball'],
score: {js: 88, php:92}
}
let proxy = new Proxy(obj, {
get(target, key){
console.log('proxy get');
return target[key];
}
});
proxy
// Proxy(Object) {name: 'mike', like: Array(2), score: {…}}
obj
// {name: 'mike', like: Array(2), score: {…}}
bj.name
//'mike'
proxy.name
// proxy get
// 'mike'
可以看到proxy打印出来的代理对象和原对象从结构上看并不一样,并且当我们分别获取name属性时,proxy会进入到get拦截方法中,但是obj并不会
也就是说这里的代理是通过非侵入的方式实现了对目标对象的数据监听,从而实现对目标对象的读写等操作,这也是proxy相比于Object.defineProperty的一个优势,(defineProperty的方式必须给对象本身的每一个属性添加监听,改变了对象本身)
给对象的属性赋值
let proxy = new Proxy({}, {
set(target,key,value){
if(!Number.isInteger(value)){
throw new Error('value is not an integer');
}
if(value > 200){
throw new Error('value seems invalid');
}
target[key] = value;
}
})
proxy.age = 100
//100
proxy.age
//100
proxy.age = ''
//Uncaught Error: value is not an integer
proxy.age = 300
//Uncaught Error: value seems invalid
删除对象的对象
let obj = {
name: 'mike',
age: 18
}
let proxy = new Proxy(obj, {
deleteProperty(target, key){
delete target[key];
return true; //表示操作成功
}
})
delete proxy.name
//true
obj
//{age: 18}
proxy
//Proxy(Object) {age: 18}
函数调用
function personFn (name, age){
console.log('my name is ' + name + ' I am ' + age + ' years old!' )
}
let proxy = new Proxy(personFn, {
apply(target, that, args){
console.log(target, that, args)
target(...args);
}
})
proxy('mike', 18)
// ƒ personFn undefined ['mike', 18]
//my name is mike I am 18 years old!
好了,我们上面仅对常用到的proxy拦截方法做展示,其他这里就不展示了,感兴趣的同学可以自己体验一下哈
通过上面的例子我们可以感受到proxy相对于Object.defineProperty更为强大,除了可以对对象进行存取的监听,还可以对删除、函数调用等进行监听操作.
还记得上一篇中展示过Object.defineProperty对数组进行set操作并不生效,实现不了监听,要想实现数据的监听我们是通过重写数组的操作方法,那么,我们现在来看看用proxy能否实现对数组的监听呢?
let list = [1,2,3,4];
let proxy = new Proxy(list, {
set(target, key, value){
console.log('proxy set');
target[key] = value;
return true;
}
})
proxy.push(100)
proxy
//Proxy(Array) {0: 1, 1: 2, 2: 3, 3: 4, 4: 100}
list
//[1, 2, 3, 4, 100]
从上面的代码可以看出,proxy可以实现对数组的监听,所以在监听数组的实现上相较于Object.defineProperty更为方便简洁.
现在我们总结一下proxy相较于Object.defineProperty的优势
- proxy代理是通过非侵入的方式实现对目标对象的数据监听,但是Object.defineProperty是通过给对象本身的每一个属性添加存取器属性来实现监听,实际上改变了对象本身
- proxy代理可以监听对象的读、写、删除、函数调用等操作,但是Object.defineProperty只能监听对象中属性的读、写,proxy功能更为强大
- proxy代理可以对数组的变化进行监听,Object.defineProperty不能监听数组的变化,实现监听的方式只能通过重新数组的方法
但是proxy对兼容性还是有要求的,旧版本的浏览器没有办法使用该功能