前言
let kind = false;
const person = new Proxy({}, {
get (target, propKey, receiver) {
if (propKey === 'kind') {
return kind = !kind;
}
return Reflect.get(target, propKey, receiver)
}
});
if (person.kind && !person.kind) {
console.log('------');
}
// ------
果然成年人的世界不是非黑即白😭(远离毒鸡汤)。
简单介绍
Proxy 是用于拦截对象访问的ES6特性。外界对代理实例的多数操作可在拦截器中捕获。
const proxy = new Proxy({}, {
get(target, propKey, receiver) { },
set(target, propKey, value, receiver) { },
has(target, propKey) { },
deleteProperty(target, propKey) { },
ownKeys(target) { },
getOwnPropertyDescriptor(target, propKey) { },
defineProperty(target, propKey, propDesc) { },
preventExtensions(target) { },
getPrototypeOf(target) { },
isExtensible(target) { },
setPrototypeOf(target, proto) { },
apply(target, object, args) { },
construct(target, args) { }
});
Proxy 支持拦截以上13种行为,详细的使用方法可以参考ES6入门-Proxy。
前言中的 Reflect ,可以理解为 Proxy 的『后门』,用于访问原始对象,同样支持13个方法,传参也与 Proxy 一致。
Proxy 很厉害,看起来可以混淆正常的逻辑判断。
使用技巧
问路
if(input.show) {
// input.show == true ?
} else {
// input.show == false ?
}
上面这段if判断在 input 是拦截器时会失效, show 可以在两次 get 给出不同的结果。如此可以穿透模块的逻辑分支,也就是投石问路的意思。输入端甚至可以通过捕获的内容输出一份日志。
const proxy = new Proxy({}, {
get (target, key) {
if (key === 'a') {
return 1;
}
}
});
console.log(JSON.parse(JSON.stringify(proxy))); // {}
不过,因为深拷贝时获取了代理的『快照』,拦截器在深拷贝后无法生效。不希望公开处理过程时,可以通过深拷贝解决。
isProxy
代理可以冒充成功是因为还无法区分拦截器和正常对象。不过拦截器状态需要对内部程序公开,经过拦截器的对象不需要再次包装拦截器。
const isProxy = Symbol();
const proxy = new Proxy({}, {
get (target, key) {
if (key === isProxy) {
return true;
}
return target[key];
}
});
console.log(proxy[isProxy]); // true
使用Symbol作为判断拦截器占用的 key 可以避免无意识下与被代理对象冲突。
Proxy 与 defineProperty
| Proxy | defineProperty | |
|---|---|---|
| 作用于 | 整个对象 | 某个属性 |
| 生效场景 | 访问属性 | 访问属性 |
| 修改原始对象 | 否 | 是 |
| IE兼容 | 12 | 9 |
| Proxy 不会替代 defineProperty,实际使用时按需求选择即可。 |