目录
定义
Proxy对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义,如属性查找、赋值、枚举、函数调用等
语法
new Proxy(target, handle);
两个参数
target是被代理的对象
handle是拦截的规则
const obj = {};
const proxy = new Proxy(obj, {
// target 目标对象;propKey 属性名;receiver 实例本身;
get: function(target, propKey, receiver) {
return 10;
}
})
console.log(proxy.a); // 打印结果是 10
console.log(proxy.b); // 打印结果是 10
console.log(obj.a); // 打印结果是 undefined
console.log(obj.b); // 打印结果是 undefined
以上代码对obj对象的get操作进行了拦截,任何读取操作都仅会返回10,且该操作只作用在代理对象proxy上,对原对象本身是不起作用的。若obj对象是不可写也不可配置的,代理对象的返回值要与被代理对象的返回值保持一致;若被代理对象没有配置get方位方法,即get方法是undefined,那么返回值必须是undefined。
handle对象常用的方法(拦截规则)
get
get
方法用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 proxy 实例本身(严格地说,是操作行为所针对的对象),其中最后一个参数可选。
1 var person = {
2 name: "张三"
3 };
4
5 var proxy = new Proxy(person, {
6 get: function(target, property) {
7 if (property in target) {
8 return target[property];
9 } else {
10 throw new ReferenceError("Property \"" + property + "\" does not exist.");
11 }
12 }
13 });
14
15 proxy.name // "张三"
16 proxy.age // 抛出一个错误
set
set
方法用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身,其中最后一个参数可选。
1 let validator = {
2 set: function(obj, prop, value) {
3 if (prop === 'age') {
4 if (!Number.isInteger(value)) {
5 throw new TypeError('The age is not an integer');
6 }
7 if (value > 200) {
8 throw new RangeError('The age seems invalid');
9 }
10 }
11
12 // 对于满足条件的 age 属性以及其他属性,直接保存
13 obj[prop] = value;
14 }
15 };
16
17 let person = new Proxy({}, validator);
18
19 person.age = 100;
20
21 person.age // 100
22 person.age = 'young' // 报错
23 person.age = 300 // 报错
上面代码中,由于设置了存值函数set
,任何不符合要求的age
属性赋值,都会抛出一个错误,这是数据验证的一种实现方法。利用set
方法,还可以数据绑定,即每当对象发生变化时,会自动更新 DOM。
apply
函数调用捕捉器
construct
new操作符的捕捉器
撤销代理对象
Proxy有一个唯一的静态方法Proxy.revocable(target, handler),可以用来创建一个可撤销的代理对象,该方法的返回值是一个对象,其结构为: {"proxy": proxy, "revoke": revoke}
。
- proxy 表示新生成的代理对象本身,和用一般方式 new Proxy(target, handler) 创建的代理对象没什么不同,只是它可以被撤销掉。
- revoke 撤销方法,调用的时候不需要加任何参数,就可以撤销掉和它一起生成的那个代理对象。
Vue为什么要用Proxy重构
Vue3.0之前,双向绑定主要是由defineProperty实现的。而defineProperty这个方法本身其实是存在不足的,比如说对于对象属性增加、数组按下标修改等一下操作无法做到原生实现。虽然Vue有提供相应的手动observer方法,但在使用体验上还是不尽如人意的。而想较于defineProperty针对属性进行拦截,Proxy直接劫持了整个对象,即不需要对特殊的操作做单独处理。
Proxy与defineProperty的对比
1.Proxy作为新标准,浏览器支持良好
2.Proxy能观察的类型比defineProperty
更丰富
3.Proxy不兼容IE,也没有polyfill,defineProperty
可以支持到IE9
4.defineProperty
劫持对象的属性,当新增属性时,需要再次defineProperty
;Proxy直接劫持整个对象,不需要额外操作。
5.defineProperty
在原对象本身进行拦截操作,而Proxy只能在生成的拦截的对象上进行拦截操作。