如何判断JS Proxy的调用/访问是否为嵌套调用?
实现嵌套访问返回Proxy、直接访问返回原对象的JS Proxy方案
哈哈,这个需求我之前也琢磨过,核心就是要区分直接获取属性值和继续链式嵌套访问这两种场景对吧?其实用Proxy的几个陷阱配合递归就能搞定,我给你写个完整的实现,再一步步拆解逻辑~
完整代码实现
function createNestedProxy(target) { // 非对象类型直接返回(比如字符串、数字、null这些,不需要代理) if (typeof target !== 'object' || target === null) { return target; } return new Proxy(target, { get(target, property, receiver) { const originalValue = Reflect.get(target, property, receiver); // 如果是基本类型,直接返回 if (typeof originalValue !== 'object' || originalValue === null) { return originalValue; } // 为当前属性值创建嵌套代理 return new Proxy(originalValue, { // 处理属性访问:嵌套访问时继续返回代理 get(nestedTarget, nestedProp, nestedRecv) { // 当触发对象转原始值/字符串时,返回原对象(比如console.log直接打印时) if (nestedProp === Symbol.toPrimitive || nestedProp === 'toString' || nestedProp === 'valueOf') { return () => nestedTarget; } // 递归创建更深层的代理 return createNestedProxy(Reflect.get(nestedTarget, nestedProp, nestedRecv)); }, // 确保属性检查、枚举等操作和原对象一致 has(nestedTarget, nestedProp) { return Reflect.has(nestedTarget, nestedProp); }, ownKeys(nestedTarget) { return Reflect.ownKeys(nestedTarget); }, getOwnPropertyDescriptor(nestedTarget, nestedProp) { return Reflect.getOwnPropertyDescriptor(nestedTarget, nestedProp); } }); }, // 直接处理赋值操作,把值存到原目标对象里 set(target, property, value, receiver) { return Reflect.set(target, property, value, receiver); } }); } // 测试示例 const data = createNestedProxy({}); data.settings = { fire: true }; console.log(data.settings); // 输出:{ fire: true }(直接返回原对象) console.log(data.settings.fire); // 输出:true(嵌套访问走Proxy逻辑)
核心逻辑拆解
- 递归代理工厂:
createNestedProxy是递归函数,只要目标是对象就创建Proxy,基本类型直接返回,确保嵌套层级都能被代理到。 - 区分访问场景:
- 当用户直接访问属性(比如
data.settings),代理会在触发对象转原始值(比如console.log打印)时,返回原对象,而不是Proxy实例。 - 当用户嵌套访问属性(比如
data.settings.fire),代理会继续递归创建下一层Proxy,直到访问到基本类型值。
- 当用户直接访问属性(比如
- 保持原对象行为:重写
has、ownKeys等陷阱,确保对代理对象做属性检查、枚举操作时,和原对象表现完全一致,避免出现意外行为。
扩展测试场景
试试多层嵌套和修改操作,验证逻辑是否正常:
// 多层嵌套测试 data.settings.app = { theme: 'dark', config: { enabled: true } }; console.log(data.settings.app); // { theme: 'dark', config: { enabled: true } } console.log(data.settings.app.config.enabled); // true // 修改属性测试 data.settings.fire = false; console.log(data.settings.fire); // false console.log(data.settings); // { fire: false, app: { ... } }
这样就能完美满足你要的需求啦~
内容的提问来源于stack exchange,提问作者Merlin -they-them-
相关产品推荐
相关产品推荐

