news 2026/6/17 13:56:38

Proxy能够监听到对象中的对象的引用吗?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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的过程

  1. 先走get(proxy, 'a')→ 拿到obj.a原始{ b: 1 },不是 Proxy

  2. 然后对这个裸对象执行.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

虽然值没变但赋值行为本身被拦截


⚠️ 几个容易踩的坑

  1. 数组的pushpop等方法:它们内部会读写length,走的是方法调用路径而非简单set,做响应式系统时通常需要额外处理(Array的陷阱更复杂,Vue 3 用的也不是纯递归 Proxy 这么简单)

  2. 必须用receiver传进Reflect.get:如果对象上有 getter 或原型链继承,漏掉 receiver 会导致this指向错误:

    // ✅ 正确 const val = Reflect.get(t, key, receiver); // ❌ 危险 const val = t[key];
  3. typeof null === 'object'​ → 判断时一定要加&& value !== null

  4. 性能:每次get都判断+可能创建 Proxy,不加缓存的话同个引用被访问 N 次就产生 N 个 Proxy 实例。用WeakMap做缓存是标准做法


一句话总结

Proxy 本身是"单层"的——它只看守你交给它的那扇门。​ 想监听对象中的对象,就得在get把每个子对象也变成 Proxy(即 Proxy Membrane / 深代理),这也就是 Vue 3 的reactive()背后的核心思想。Proxy 不是不能,是需要你主动递归地"铺网"。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/17 13:54:23

H3-Py:基于Cython的Uber H3地理空间索引系统Python绑定架构解析

H3-Py:基于Cython的Uber H3地理空间索引系统Python绑定架构解析 【免费下载链接】h3-py Python bindings for H3, a hierarchical hexagonal geospatial indexing system 项目地址: https://gitcode.com/gh_mirrors/h3/h3-py H3-Py是Uber开源的H3六边形层次地…

作者头像 李华
网站建设 2026/6/17 13:31:48

大数据专业适合冲一冲还是稳一稳

大数据专业适合冲一冲还是稳一稳大数据专业作为近年来的热门方向,其就业前景和学习难度是学生与从业者关注的焦点。选择“冲一冲”还是“稳一稳”需结合个人职业规划、学习能力及行业需求综合判断。以下从多个维度分析,帮助决策。行业需求与就业前景指标…

作者头像 李华
网站建设 2026/6/17 13:22:49

Expert电子实验室--51单片机核心板PCB布线

一、开篇重中之重:永远不要使用自动布线!很多新手误区:元器件摆放完成后,直接点击软件自动布线,觉得一键出板、省时省力。郑重提醒(三遍强调):❌ 绝对不要用自动布线!❌ …

作者头像 李华
网站建设 2026/6/17 13:14:10

用ToothGrowth数据集讲透贝叶斯统计底层逻辑

1. 项目概述:用“长牙”讲透贝叶斯统计的底层逻辑 你有没有过这种感觉:翻开一本统计学教材,满页都是“先验分布”“后验概率”“马尔可夫链蒙特卡洛”,越看越像在读天书?或者在R里敲下 stan_glm() ,跑出一…

作者头像 李华