Skip to content
概述
“无穷方法对象”来自 LeetCode 第 2690 题,要求构造一个对象,使得 obj.anything() 返回 "anything";链式调用 obj.a.b.c() 返回最后一层属性名 "c"。实现依赖 Proxy 的 get 与 apply 陷阱。
基本概念
Proxy 可以拦截对象上的底层操作,每个拦截行为对应一个陷阱(trap)函数。本题主要用到两个陷阱:
get(target, property, receiver):拦截属性读取。apply(target, thisArg, argumentsList):拦截函数调用(target必须是可调用的)。
工作原理
如果只要求单级调用 obj.hello(),在 get 陷阱中直接返回 () => property 就能满足:
js
function createInfiniteObject() {
return new Proxy({}, {
get(target, property) {
return () => String(property);
}
});
}
const obj = createInfiniteObject();
obj.hello(); // "hello"
obj.world(); // "world"但链式调用 obj.a.b.c() 会失败。obj.a 得到的是一个普通函数,接着访问 .b 是在该函数对象上读取属性,并不会触发 Proxy 的 get 陷阱,所以返回 undefined。于是 obj.a.b 这一步就会因为对 undefined 继续读取属性而抛出 TypeError,根本走不到最后的 () 调用。
要使链式访问每一步都返回一个既能继续被属性访问拦截、又能作为函数调用的对象,需要让 get 每次都返回一个新的 Proxy,并且这个 Proxy 的目标是一个函数(以便 apply 被触发)。同时,每次创建 Proxy 时内部记录当前的属性名,最终在 apply 陷阱中返回最后一个属性名。
基本用法
ts
function createInfiniteObject(): any {
const handler: ProxyHandler<any> = {
get(_target, prop) {
return createInfiniteObjectWithProp(String(prop));
},
apply(_target, _thisArg, _args) {
return _target.__prop;
}
};
function createInfiniteObjectWithProp(prop = '') {
const target = function() {};
target.__prop = prop;
return new Proxy(target, handler);
}
return createInfiniteObjectWithProp();
}调用示例:
js
const obj = createInfiniteObject();
obj.hello(); // "hello"
obj.a.b.c(); // "c"
obj.any.thing(); // "thing"调用链 obj.a.b.c() 的执行过程:
obj.a→ 触发get,prop为"a",返回一个__prop为"a"的新 Proxy。.b→ 在上一步返回的 Proxy 上触发get,prop为"b",返回__prop为"b"的新 Proxy。.c→ 同理,得到__prop为"c"的 Proxy。()→ 触发apply陷阱,返回_target.__prop,即"c"。
每次属性访问都会丢弃上一次记录的名称,因此永远只保留最后一段属性名。
示例
js
const o = createInfiniteObject();
// 单级调用
o.status(); // "status"
// 多级调用
o.api.v1.users(); // "users"
// 数字属性名
o[100](); // "100"
// 属性名含空格
o["hello world"](); // "hello world"
// 中间变量不影响最终调用
const mid = o.x.y;
mid.z(); // "z"注意点
- 链式调用只保留最后一个属性名。题目定义如此,与实现无关。
get陷阱中的property可能为Symbol类型,调用String(prop)可保证一律转为字符串返回。- 如果
get返回的是普通函数,将失去后续属性访问的拦截能力。 - 要使 Proxy 自身能被调用,
target必须是一个函数,且需配合apply陷阱;否则obj.a.b()会因为 Proxy 不可调用而抛出TypeError。
应用
这种“无限方法对象”模式主要用于展示 Proxy 的能力。实际工程中,类似思路可用于:
- 测试替身:按需生成任意方法名的 mock 对象,不必逐一声明桩函数。
- 动态 API 适配层:将外部 API 路径映射为链式调用的对象,减少硬编码的字符串拼接。
- 响应式系统原型:Vue 3 的响应式引擎用
Proxy拦截读写,结合effect做依赖追踪。这里的get/apply组合只是 Proxy 灵活性的一个侧面。
