proxy 拦截需平衡控制力与稳定性,核心是围绕场景设计 trap 并遵守正确转发、防死循环、明边界三大规则;必须用 reflect 实现转发,按需代理嵌套对象,精选 get/set/has/ownkeys/apply 五类 trap,封装为可复用函数。

Proxy 拦截操作不是“写个 handler 就完事”,关键在于控制力与稳定性之间的平衡。真正可靠的做法,是围绕目标场景设计 trap 行为,同时守住几个底层规则:正确转发、避免死循环、明确代理边界。
用 Reflect 正确转发默认行为
几乎所有 trap(尤其是 get/set/apply)都必须配合 Reflect 使用,否则容易破坏原型链、丢失 this 或引发无限递归。
- get 中别写 target[prop],改用 Reflect.get(target, prop, receiver) —— receiver 确保 getter 内部的 this 指向代理对象而非原始对象
- set 必须显式返回布尔值:return Reflect.set(target, prop, value, receiver);返回 false 会静默失败(严格模式下抛错)
- apply 中调用原函数要用 Reflect.apply(target, thisArg, args),而不是 target.apply(...),避免绕过代理链
只代理需要拦截的一层,不盲目递归
Proxy 默认只代理目标对象本身,嵌套对象、数组元素、新增属性都不会自动被拦截 —— 这不是缺陷,而是可控性的体现。
- 若需深度响应式(如 Vue3),在 get 中判断返回值是否为对象,再按需包装:typeof value === 'object' && value !== null ? new Proxy(value, handler) : value
- 注意循环引用:包装前可用 WeakMap 缓存已代理对象,避免重复代理或栈溢出
- 数组索引赋值(arr[0] = x)、length 修改、push/pop 等方法调用不会触发 set —— 如需拦截,得在 get 中识别数组方法并包装其返回函数
按需选择 trap,别堆砌无用逻辑
13 种 trap 全部定义不仅没必要,还会拖慢性能。业务中高频且实用的就五个:
- get:兜底默认值、权限过滤、依赖收集
- set:字段校验(如邮箱格式)、变更通知、只读保护
-
has:让属性“存在但不可见”(
prop in proxy返回 false) - ownKeys:动态控制 Object.keys() 和 for...in 的枚举结果
- apply:函数调用埋点、缓存、参数预处理
避免污染原生原型,优先封装为可复用函数
直接修改 Array.prototype.push 或 Object.prototype.toString 是危险操作,尤其在第三方库共存环境中。
- 把拦截逻辑封装成工厂函数,例如:createValidatedProxy(obj, rules) 或 logCalls(fn, name)
- 对内置类型(如 Map/Set)做拦截时,不要试图代理整个实例,而是代理其方法调用路径(在 get 中识别 .set/.get 并返回包装函数)
- 敏感场景(如权限控制)建议配合 Symbol 键或私有字段做标记,避免 key 名冲突
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











