Proxy
>Proxy 对象允许你为另一个对象创建代理,该代理能够拦截并重新定义该对象的基本操作。
描述
Proxy 对象允许你创建一个可替代原始对象的对象,但该对象可能重定义获取、设置和定义属性等基础 Object 操作。代理对象常用于记录属性访问、验证、格式化或清理输入等场景。
创建 Proxy 需提供两个参数:
target:需要代理的原始对象handler:定义哪些操作将被拦截以及如何重定义拦截操作的对象
例如,此段代码为 target 对象创建了代理:
const target = {
message1: "大家",
message2: "好",
};
const handler1 = {};
const proxy1 = new Proxy(target, handler1);
由于 handler 是空的,此代理的行为如同直接对源对象进行操作:
console.log(proxy1.message1); // 大家
console.log(proxy1.message2); // 好
要自定义代理,我们在 handler 对象中定义函数:
const target = {
message1: "大家",
message2: "好",
};
const handler2 = {
get(target, prop, receiver) {
return "你好世界";
},
};
const proxy2 = new Proxy(target, handler2);
这里我们提供了一个 get() 处理器的实现,它会拦截对目标对象属性访问的尝试。
处理器函数有时被称为陷阱,大概是因为它们会捕获对目标对象的调用。上文 handler2 中的陷阱重新定义了所有属性访问器:
console.log(proxy2.message1); // 你好世界
console.log(proxy2.message2); // 你好世界
代理常与 Reflect 对象配合使用,该对象提供了一些与 Proxy 陷阱同名的方法。Reflect 方法通过调用对应的对象内部方法来实现反射语义。例如,若不希望重定义对象行为,可调用 Reflect.get:
const target = {
message1: "大家",
message2: "好",
};
const handler3 = {
get(target, prop, receiver) {
if (prop === "message2") {
return "你好世界";
}
return Reflect.get(...arguments);
},
};
const proxy3 = new Proxy(target, handler3);
console.log(proxy3.message1); // 大家
console.log(proxy3.message2); // 你好世界
Reflect 方法仍通过对象内部方法与对象交互——若在代理上调用该方法,它不会“解除代理化”。若在代理陷阱中使用 Reflect 方法,且该方法调用再次被陷阱拦截,则可能引发无限递归。
术语
以下术语用于描述代理的功能特性。
对象内部方法
对象是属性的集合。然而,该语言并未提供任何机制来直接操作对象中存储的数据——相反,对象定义了一些内部方法来规定其交互方式。例如,当你读取 obj.x 时,你可能会期望发生以下情况:
x属性会沿着原型链上行搜索,直至找到该属性。- 若
x是数据属性,则返回属性描述符的value属性。 - 若
x是访问器属性,则调用获取器,并返回获取器的返回值。
这种过程在语言中并无特殊之处——仅仅是因为普通对象默认具有一个名为 [[Get]] 的内部方法,该方法即以这种行为方式定义。obj.x 属性访问语法只是调用了对象的 [[Get]] 方法,而对象会通过自身内部方法的实现来决定返回什么内容。
另一个例子是,数组与普通对象不同,因为它们具有一个神奇的 length 属性——当修改该属性时,系统会自动为数组分配空槽位或移除元素。同样地,向数组添加元素会自动改变 length 属性。这是因为数组拥有 [[DefineOwnProperty]] 内部方法,该方法在写入整数索引时会更新 length,在写入 length 值时则更新数组内容。这类内部方法实现与普通对象不同的特殊对象被称为特殊对象。Proxy 使开发者能够全权定义自定义的特殊对象。
所有对象均具有以下内部方法:
| 内部方法 | 对应的陷阱 |
|---|---|
[[GetPrototypeOf]] |
getPrototypeOf() |
[[SetPrototypeOf]] |
setPrototypeOf() |
[[IsExtensible]] |
isExtensible() |
[[PreventExtensions]] |
preventExtensions() |
[[GetOwnProperty]] |
getOwnPropertyDescriptor() |
[[DefineOwnProperty]] |
defineProperty() |
[[HasProperty]] |
has() |
[[Get]] |