Proxy
receiver
- 改变 this
详情 - 能够获取实际调用的对象
- 在大多数情况下,receiver 就是代理对象本身。然而,如果代理对象是另一个对象的原型,那么 receiver 就变成了调用属性的那个对象。
const target = {
message: 'Hello, world'
};
const proxy = new Proxy(target, {
get(targ, prop, receiver) {
}
});
const child = Object.create(proxy);
console.log(proxy.message);
console.log(child.message);
优点
- 功能强大:Proxy 提供了多种拦截场景,包括读写属性、删除属性、函数调用、原型链操作等。
- 拦截细致:Proxy 可以有效地代理和拦截数组对象,包括索引访问和修改等。
- 拦截简单:对一个对象应用拦截行为时,只需创建一个 Proxy
- Object.defineProperty 需要为每个属性创建单独的属性描述符。
- 不污染原目标对象 :Proxy 返回的是一个新对象,可以在不影响原始对象的前提下对代理对象操作。原始对象和代理对象可互相独立存在。而 Object.defineProperty 直接作用于原始对象。代理对象也不影响 instanceof 操作
- 不需要事先知道对象的结构,代理是在对象外层的抽象层。代理可以在运行时决定,而不用在事前就定义
- 拦截原型链:Proxy 可以拦截对原型链上的操作,而 Object.defineProperty 只作用于单个对象
- 传递性:如果代理对象再次被代理,后面的代理能够完整地响应之前代理上的拦截行为。而 Object.defineProperty 不具备这样的特性
- 可撤销性:使用 Proxy.revocable() 创建的代理对象,可以随时取消(撤销)代理的行为,恢复到普通的对象操作,而 Object.defineProperty 一旦定义了就不能这么轻易撤销
Proxy 懒代理
- 只有当访问到对象的某个层级时,那个特定的层级对象才会被代理。这意味着代理创建是按需进行的
function createLazyProxy(target, handler) {
return new Proxy(target, {
get(target, property, receiver) {
const value = Reflect.get(target, property, receiver);
if (value && typeof value === 'object' && !value._isProxy) {
const proxy = createLazyProxy(value, handler);
proxy._isProxy = true;
return proxy;
}
return value;
},
set(target, property, value, receiver) {
if (value && typeof value === 'object') {
value = createLazyProxy(value, handler);
}
return Reflect.set(target, property, value, receiver);
},
...handler
});
}
const handler = {
set(target, property, value) {
console.log(`Property ${property} set to ${value}`);
target[property] = value;
return true;
}
};
const obj = { level1: { level2: { level3: 'initial' } } };
const proxyObj = createLazyProxy(obj, handler);
console.log(proxyObj.level1.level2.level3);
proxyObj.level1.level2.level3 = 'changed';
缺点
- 性能影响:由于 Proxy 拦截操作涉及额外的计算,可能导致程序速度略有降低
- 兼容性问题:即使使用 Polyfill,也无法完全模拟 Proxy 的原生行为。
Reflect
- 使用 Reflect 有助于确保 Proxy 的处理器方法正确地模拟了 JavaScript 默认的操作,从而避免了由于自定义操作引入的可能的行为差异或者错误
const strictModeProxy = new Proxy(obj, {
set(target, prop, value) {
if (!Reflect.set(target, prop, value)) {
throw new Error(`Property ${prop} is not writable.`);
}
return true;
}
});
strictModeProxy.readOnly = 2;