Proxy是JavaScript中唯一能真正拦截并重写基本操作的机制,通过trap函数接管[[Get]]、[[Set]]等内部方法,需用new Proxy(target, handler)创建,无法代理原始值且仅代理第一层。
Proxy 不是语法糖,也不是对象增强工具,它是 JavaScript 中唯一能真正拦截并重写基本操作(如读取、赋值、in、delete)的机制。
当你对一个对象做 obj.prop、obj.prop = value、prop in obj、delete obj.prop 或 Object.keys(obj) 这类操作时,JavaScript 引擎会触发对应的内部方法(如 [[Get]]、[[Set]]、[[HasProperty]])。Proxy 就是通过 trap(陷阱)函数把这些内部方法的调用接管过来。
关键点:
new Proxy(target, handler) 创建新对象,不能直接修改原对象行为target 可以是普通对象、数组、函数甚至 null(但多数 trap 会报错)handler 是一个普通对象,键名是 trap 名(如 get、set、has),值是对应函数get 和 set 是使用频率最高的两个 trap,但它们的参数和返回值有明确约定,写错会导致静默失败或无限递归。
常见错误:
get 中直接访问 target[prop] 而不通过 Reflect.get(),可能绕过其他 trap 或丢失 receiverset 中忘记返回布尔值(必须返回 true 表示成功,否则严格模式下抛 TypeError)set 的第三个参数 receiver 总是 proxy 本身——它可能是继承链上的子对象正确写法示例:
const obj = { count: 0 };
const proxy = new Proxy(obj, {
get(target, prop, receiver) {
console.log('读取', prop);
return Reflect.get(target, prop, receiver);
},
set(target, prop, value, receiver) {
console.log('设置', prop, '为', value);
return Reflect.set(target, prop, value, receiver); // 必须返回 boolean
}
});
Object.defineProperty 只能监听**已存在的属性**的读写,且无法拦截 in、delete、for...in、Object.keys()、构造函数调用等行为。
而 Proxy 的 trap 覆盖更广:
has 拦截 prop in proxy
deleteProperty 拦截 delete proxy.prop
ownKeys 拦截 Object.getOwnPropertyNames()、for...in
apply 和 construct 分别拦截函数调用和 new 调用getOwnPropertyDescriptor 控制 Object.getOwnPropertyDescriptor() 返回值
注意:ownKeys 返回的必须是数组,且需包含所有 enumerable 为 true 的自有属性(否则 for...in 行为可能异常)。
Proxy 很强大,但不是万能的,几个关键限制常被低估:
=== 或 == 与原对象比较相等(它们是不同对象)undefined、primitive(如字符串、数字),只能代理对象Array.prototype.push)内部不走标准 [[Set]],而是直接操作索引,导致 set trap 可能不触发最常被忽略的一点:Proxy 只代理第一层。如果 proxy.nested 是个普通对象,它的操作不会被自动拦截——需要手动递归包装或使用 Proxy.revocable 配合懒代理策略。