前言
Proxy 也就是代理,一般叫他拦截器。
Proxy 除读写外还可以监听属性的删除,方法的调用等(delete 对象的属性,给对象的属性赋值等)。
例如对数据的处理,对构造函数的处理,对数据的验证,在访问对象前添加了一层拦截,可以过滤很多操作。
Proxy 区别于 Object.definedProperty。
Object.defineProperty 只能监听到属性的读写,而 Proxy 除读写外还可以监听属性的删除,方法的调用等。
语法
let myProxy = new Proxy(target, handler);
//handler中的三个方法
let myProxy = new Proxy(target, {
get(target, key) {
return ***;
},
set(target, key, value) {
return boolean //必须为布尔值
},
deleteProperty(target,key){
return boolean //必须为布尔值
}
});
target: 需要使用Proxy包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
handler: 一个对象,其属性是当执行一个操作时定义代理的行为的函数(可以理解为某种触发器)。
handler中有三个方法: get()、set()、deleteProperty()。一旦调用实例的对象时,就会触发这几个方法。但是调用实例不会
特点。
- set和deleteProperty中的代理拦截设置,只会拦截实例(以下为myProxy )的不正当操作,但不会对target(以下为me)的操作拦截。
- 修改实例会一起修改target,修改target也会影响实例,相互影响。
- set和deleteProperty最后必须要返回一个
boolean
值
get获取读写,简单赋值:
let me = {
name: "zzz",
age: "18"
};
let myProxy = new Proxy(me, {
get(target, key) {
let result = target[key];
console.log('获取了getter属性');
if (key === "age") result += "岁";
return result;
},
});
console.log(me.name);
上方的案例,我们首先创建了一个me 对象,里面有name属性,然后我们使用Proxy将其包装起来,再返回给myProxy ,此时的myProxy 已经成为了一个Proxy实例,我们对其的操作,都会被Proxy拦截。
Proxy有两个参数,第一个是target,也就是我们传入的*test对象,另一个则是handler,也就是我们传入的第二个参数,一个匿名对象。在handler中定义了一个名叫get的函数,当我们获取 myProxy 的属性时,则会触发此函数。
set拦截
let me = {
name: "zzz",
age: "18"
};
let myProxy= new Proxy(me, {
get(target, key) {
let result = target[key];
//如果是获取 年龄 属性,则添加 岁字
if (key === "age") result += "岁";
return result;
},
set(target, key, value) {
if (key === "age" && typeof value !== "number") {
throw Error("age字段必须为Number类型");
}
return Reflect.set(target, key, value);//与target[key] = value; return true;功能一致。 set中return 必须返回的是 boolean值
}
});
return Reflect.set(target, key, value);//与target[key] = value; return true;功能一致。 set中return 必须返回的是 boolean值
Reflect传送门滴滴滴,现在还没有
1. 查看myProxy.name和myProxy.age的值
console.log(我叫${myProxy.name} 我今年${myProxy.age}了
);
2. 修改myProxy和修改me的区别
2.1. 将myProxy.age修改为“aa”
,无法修改成功,报错。myProxy还是之前的样子。
由于在myProxy中set中的设置,当myProxy的age不是number类型时,会抛出一个错误throw Error("age字段必须为Number类型");
导致myProxy.age无法修改,最后的值还是18岁
2.2. 将myProxy.age修改为17
,修改成功
2.3. 修改target参数,me.age = "eighteen"
明显target的修改不受Proxy中set方法的约数,不管是否为Number类型都可以修改成功。
综上所述,Proxy中set只会拦截实例(myProxy),过滤对myProxy不正当操作。单并不会影响target(me)的修改。这也证实了,只有调用实例的属性时,才会触发handle
deleteProperty删除拦截
let me = {
name: "zzz",
age: "18"
};
let myProxy= new Proxy(me, {
get(target, key) {
let result = target[key];
//如果是获取 年龄 属性,则添加 岁字
if (key === "age") result += "岁";
return result;
},
deleteProperty(target,key){
//设置为只允许删除 a 属性
if(key === 'name'){
//对于proxy对象,真正删除的是在deleteProperty中执行
delete target[key];
return true;
}else {
return target[key];
}
}
});
1. 执行delete myProxy.age;
由于上述deleteProperty
方法定义中,只有当key === 'name'
才能删除实例myProxy的属性,所以执行delete myProxy.age;
方法被拦截,无法删除
2. 执行delete myProxy.name;
可以删除
3. 执行delete me.age;
由于deleteProperty
只对实例拦截有效对于target无效,所以在me
中可以删除任意属性
对原对象的影响(对target的影响)的影响:
上述在proxy中,myProxy成为了proxy的实例,target是myProxy的参数。
-
修改myProxy中的属性
myProxy.age修改为17
后,me.age也变成了17
-
修改me中的属性
在对于me.age修改为3
后,myProxy中Proxy中age也变成了3
综上所述,互相影响
其他例子:
let arr = [7, 8, 9];
let newArr = new Proxy(arr, {
set(target, key, val) {
if (typeof(val) === 'number') {
target[key] = val
return true
} else {
throw Error("数组必须传入Number类型的值");
}
}
})