Proxy 是唯一能拦截动态属性增删、in 操作、数组方法及全部 13 种操作的机制;Object.defineProperty 仅支持已有属性的 getter/setter,无法代理新增属性或数组索引赋值。

JavaScript 的 Proxy 不是语法糖,也不是可选的“高级技巧”——当你需要拦截对象读写、验证字段、实现响应式更新或调试属性访问时,它就是唯一正解。
什么时候必须用 Proxy 而不是普通对象或 Object.defineProperty
普通对象无法拦截新增属性、删除属性、in 操作符、for...in 遍历;Object.defineProperty 只能对已存在的属性设 getter/setter,且不支持数组索引赋值拦截。而 Proxy 可以覆盖全部 13 种基本操作(trap)。
- 想监听
obj.newProp = 123这种动态添加的属性?只有set+definePropertytrap 配合has才行 - 要让
'x' in obj返回自定义逻辑?必须用hastrap - 需要代理整个数组并捕获
arr.push()?得靠gettrap 拦截方法调用,并重写返回的函数
Proxy 最小可用示例:拦截读写并打印日志
别一上来就堆 Reflect 和所有 trap,先跑通最核心的 get 和 set:
const target = { count: 0 };
const handler = {
get(obj, prop) {
console.log(`读取 ${prop}`);
return obj[prop];
},
set(obj, prop, value) {
console.log(`设置 ${prop} = ${value}`);
obj[prop] = value;
return true; // 必须返回 true 表示赋值成功
}
};
const proxy = new Proxy(target, handler);
proxy.count = 5; // 输出:设置 count = 5
console.log(proxy.count); // 输出:读取 count → 5
注意:set trap 中若不返回 true,严格模式下会抛出 TypeError: 'set' on proxy: trap returned falsish for property。
立即学习“Java免费学习笔记(深入)”;
为什么不能直接在 get 里 return 一个新对象?
常见错误是这样写:get() { return { a: 1 }; }——这会导致每次访问都返回新对象,引用丢失,深层响应式失效。正确做法是缓存或用 Reflect.get 委托原行为:
- 想包装返回值(比如加一层只读):用
Reflect.get(target, prop, receiver)获取原值,再处理 - 代理嵌套对象时,对非原始值返回新的
Proxy实例,否则内层无法被拦截 -
receiver参数常被忽略,但它决定this指向,尤其在访问 getter 或方法时至关重要
真实项目中容易踩的坑
Proxy 是浅代理,只代理第一层;不能代理 undefined 或原始值(如字符串字面量);instanceof 在代理后可能失效(需用 getPrototypeOf trap 修复);V8 对代理对象的优化程度低于普通对象,高频场景(如游戏循环)慎用。
最隐蔽的问题:代理后的对象与原对象不相等(proxy !== target),JSON 序列化会跳过不可枚举/不可配置属性,且 Proxy 本身不能被 JSON.stringify 直接序列化——需要显式定义 toJSON 方法或用 structuredClone(仅现代环境)。










