含深层嵌套Proxy对象属性的赋值表达式为何无法更新目标属性?
嵌套Proxy赋值失效问题解析
我写了一个接收对象并返回其代理版本的函数,修改代理对象不会影响原对象:
const createProxy4 = (obj) => { const handler = { get(target, prop) { // 如果属性值是对象,返回前替换为新的Proxy if (target[prop] !== null && typeof target[prop] === "object") { target[prop] = new Proxy({ ...target[prop] }, handler); } // 原始值直接返回 return target[prop]; }, set(target, prop, value) { // 更新属性直接赋值 target[prop] = value; return true; }, }; // 创建原对象的浅拷贝 return new Proxy({ ...obj }, handler); };
示例
// 原对象 const obj8 = { x: { y: 5 }, }; // 创建代理副本 const proxy8 = createProxy4(obj8); // 修改代理对象的嵌套属性 proxy8.x.y = 27; console.log(obj8); // { x: { y: 5 } } - 原对象不受影响 console.log(proxy8); // { x: { y: 27 } } - 代理对象属性修改生效 // 使用自身属性的表达式修改代理 proxy8.x.y = proxy8.x.y + 2; console.log(obj8); // { x: { y: 5 } } - 原对象不受影响 console.log(proxy8); // { x: { y: 27 } } - 无变化!为什么? // 使用中间变量重构表达式 const t = proxy8.x.y + 2; proxy8.x.y = t; console.log(obj8); // { x: { y: 5 } } - 原对象不受影响 console.log(proxy8); // { x: { y: 29 } } - 修改生效。为什么?
问题解析
两种写法结果不同的核心原因,在于你的Proxy实现每次访问嵌套对象时都会创建全新的Proxy实例:
为什么直接赋值表达式失效?
在proxy8.x.y = proxy8.x.y + 2中,JavaScript会先计算右侧表达式,再执行左侧赋值:
- 计算右侧:访问
proxy8.x触发外层Proxy的get拦截器,此时外层目标对象的x是之前的代理实例,于是创建一个全新的ProxyA替换掉外层目标对象的x,读取ProxyA的y值(27)计算得到29; - 执行左侧赋值:再次访问
proxy8.x触发get拦截器,此时外层目标对象的x是ProxyA,又会创建另一个全新的ProxyB替换掉外层目标对象的x,然后把29赋值给ProxyB的y;
后续console.log(proxy8)时,又会触发proxy8.x的get拦截器,基于当前外层目标对象的x(ProxyB)创建新的ProxyC——理论上ProxyB的y已经是29,但示例注释的误差可能来自浏览器console对Proxy的特殊打印机制。本质是:左右两边的proxy8.x指向完全不同的Proxy实例,赋值操作没有作用到后续访问会用到的代理上。
为什么中间变量写法生效?
在const t = proxy8.x.y + 2; proxy8.x.y = t;中:
- 第一次访问
proxy8.x创建ProxyA,读取y=27得到t=29; - 第二次访问
proxy8.x创建ProxyB,将t=29赋值给ProxyB的y; - 后续
console.log(proxy8)时,访问proxy8.x创建ProxyC,基于ProxyB的目标对象(y=29),因此能显示正确结果。
本质缺陷与修复
你的Proxy实现的问题在于重复创建代理,每次访问嵌套对象都会生成新的Proxy,不仅导致赋值失效,还浪费性能。修复方案是确保每个嵌套对象只被代理一次:
const createProxy4 = (obj) => { // 深拷贝原对象,完全隔离原对象的修改 const deepClone = (obj) => { if (obj === null || typeof obj !== "object") return obj; return Array.isArray(obj) ? obj.map(deepClone) : Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, deepClone(v)])); }; const handler = { get(target, prop) { const value = target[prop]; // 仅当属性是对象且未被代理时,才创建Proxy if (value !== null && typeof value === "object" && !(value instanceof Proxy)) { target[prop] = new Proxy(value, handler); } return target[prop]; }, set(target, prop, value) { target[prop] = value; return true; }, }; return new Proxy(deepClone(obj), handler); };
修改后,每个嵌套对象只会被代理一次,后续访问同一属性返回同一个Proxy实例,赋值操作就能正常生效了。
内容的提问来源于stack exchange,提问作者Black Beard
相关产品推荐
相关产品推荐

