代理陷阱
JS引擎内部有很多底层对象操作,比如读取一个属性值或者写入一个属性等操作,这些操作我们很难去改变的。所以为了让我们有机会改变这些默认的操作,JS对这些操作都设置了代理陷阱。
完整的代理陷阱可以参照 MDN
| 代理陷阱 | 覆写的特性 | 默认特性 |
|---|---|---|
| get | 读取一个属性值 | Reflect.get() |
| set | 写入一个属性 | Reflect.set() |
| has | in操作符 | Reflect.has() |
| deleteProperty | delete操作符 | Reflect.deleteProperty() |
| getPrototypeOf | Object.getPrototypeOf() | Reflect.getPrototypeOf() |
| setPrototypeOf | Object.setPrototypeOf() | Reflect.setPrototypeOf() |
| isExtensible | Object.isExtensible() | Reflect.isExtensible() |
| preventExtensions | Object.preventExtensions() | Reflect.preventExtensions() |
| getOwnPropertyDescriptor | Object.getOwnPropertyDescriptor() | Reflect.getOwnPropertyDescriptor() |
| defineProperty | Object.defineProperty() | Reflect.defineProperty() |
| ownKeys | Object.keys()/Object.getOwnPropertyNames()/Object.getOwnPropertySymbols() | Reflect.ownKeys() |
| apply | 调用一个函数 | Reflect.apply() |
| construct | 用new调用一个函数 | Reflect.construct() |
Proxy
使用 Proxy 可以拦截对象的代理陷阱,从而改变对象的默认操作。
创建
new Proxy() 构造函数接收两个参数,目标对象 和 代理陷阱处理对象。
let target = {};
let proxy = new Proxy( target, {});
这里的 proxy 不会存储任何属性,在这个 proxy 上进行的任何操作都会被转发给目标对象。
Reflection
每个代理陷阱对应一个命名和参数都相同的 Reflect 方法,他可以替代这些对象的默认操作。也就是说 Reflection 是默认就有的,不需要我们手动创建,可以参考代理陷阱的表格。
Demo
对于这些概念看起来挺复杂的,但是我可以可以通过一个案例,对 set 陷阱添加验证来说明,代理和反射的用法。
set 陷阱接收4个参数:
trapTarget,用于接收属性的对象key,要写入的属性键value,被写入的值receiver操作发生的对象
let target = { name: "target" };
let proxy = new Proxy( target, {
set(trapTarget, key, value, receiver) {
// 忽略不希望受到影响的已有属性
if (!trapTarget.hasOwnProperty(key)) {
if (isNaN(value)) {
throw new TypeError('属性必须是数字');
}
}
// 通过反射执行默认的set操作
return Reflect.set(trapTarget, key, value, receiver);
}
});
经过这样的代理,我们给 target 对象添加了 set 陷阱处理,当再往 target 对象上写入一个属性的时候,会先判断是否为数字,是的话执行写入对象的默认操作,不是数字抛出错误。
proxy.count = 1;
console.log(proxy.count); // 1
console.log(target.count); // 1