代理陷阱
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