Proxy能够监听到对象中的对象的引用吗?
简短结论原生的new Proxy(target, handler)只能代理「它直接包裹的那一层对象」对target内部的嵌套对象默认是透传的——返回的是裸对象后续操作完全逃逸监听。为什么会听不到嵌套对象const obj { a: { b: 1 } }; const proxy new Proxy(obj, { get(t, k) { console.log(get, k); return Reflect.get(t, k); }, set(t, k, v) { console.log(set, k, v); return Reflect.set(t, k, v); } }); proxy.a.b 99; // 只触发了一次 get(a)返回的是原始裸对象 { b: 1 } // set(b) 永远不会触发执行proxy.a.b 99的过程先走get(proxy, a)→ 拿到obj.a原始{ b: 1 }不是 Proxy然后对这个裸对象执行.b 99→ 跟 Proxy 毫无关系所以不是 Proxy 能力不够而是它根本没有机会介入第二步——因为第一步返回的就不是代理对象。✅ 解法递归代理Proxy Membrane 模式核心思路在get拦截器中凡是读到的值是对象就再给它套一层 Proxy让整条访问链上的每一层都是代理过的function deepProxy(target, handler) { // 缓存避免重复代理 处理循环引用 const cache new WeakMap(); function makeProxy(obj) { if (obj null || typeof obj ! object) return obj; if (cache.has(obj)) return cache.get(obj); const proxy new Proxy(obj, { get(t, key, receiver) { const val Reflect.get(t, key, receiver); // 读到子对象 → 递归代理后返回 return (val ! null typeof val object) ? makeProxy(val) : val; }, set(t, key, value, receiver) { const oldVal t[key]; const result Reflect.set(t, key, value, receiver); handler?.onChange?.({ type: SET, path: key, oldValue: oldVal, newValue: value }); return result; }, deleteProperty(t, key) { const had key in t; const oldVal t[key]; const result Reflect.deleteProperty(t, key); if (had) handler?.onChange?.({ type: DELETE, path: key, oldValue: oldVal }); return result; } }); cache.set(obj, proxy); return proxy; } return makeProxy(target); }使用效果const state deepProxy({ a: { b: 1 }, list: [10, 20] }, { onChange: ({ type, path, oldValue, newValue }) console.log([${type}] ${path}:, oldValue, →, newValue) }); state.a.b 99; // ✅ 能捕获(经过递归代理的 a 的 set 触发) state.list.push(30); // ⚠️ 数组的 push 本质是方法调用set trap 不一定按你想的方式触发 state.a { c: 2 }; // ✅ 外层 set 正常捕获替换整个子对象引用两种引用变化要区分清楚场景能否被外层 Proxy 的set捕获说明proxy.a { c: 2 }替换整个子对象引用✅能​这是 proxy 自身的属性赋值走set(proxy, a, ...)proxy.a.b 99修改子对象内部属性❌不能除非递归代理操作的是子对象外层 proxy 根本碰不到proxy.a proxy.a把子对象重新赋回✅ 能触发 set虽然值没变但赋值行为本身被拦截⚠️ 几个容易踩的坑数组的push、pop等方法它们内部会读写length走的是方法调用路径而非简单set做响应式系统时通常需要额外处理Array的陷阱更复杂Vue 3 用的也不是纯递归 Proxy 这么简单必须用receiver传进Reflect.get如果对象上有 getter 或原型链继承漏掉 receiver 会导致this指向错误// ✅ 正确 const val Reflect.get(t, key, receiver); // ❌ 危险 const val t[key];typeof null object​ → 判断时一定要加 value ! null性能每次get都判断可能创建 Proxy不加缓存的话同个引用被访问 N 次就产生 N 个 Proxy 实例。用WeakMap做缓存是标准做法一句话总结Proxy 本身是单层的——它只看守你交给它的那扇门。​ 想监听对象中的对象就得在get里把每个子对象也变成 Proxy即 Proxy Membrane / 深代理这也就是 Vue 3 的reactive()背后的核心思想。Proxy 不是不能是需要你主动递归地铺网。

相关新闻