Proxy 是创建新代理对象以拦截对其的操作,而非修改原对象;需通过 handler 显式定义 get、set、ownKeys 等 trap,且 get/set 中须用 Reflect 方法并正确处理 receiver 参数。

Proxy 是什么,它真能“拦截”操作?
Proxy 不是给对象加个钩子就自动生效的魔法,它创建的是一个全新对象,所有对它的访问(读、写、遍历、构造等)都会经过你定义的 handler。原对象本身完全不受影响——Proxy 拦截的是对代理对象的操作,不是对目标对象的操作。
常见误解是以为 const p = new Proxy(obj, handler) 会让 obj 变得可拦截,其实 obj 依然裸奔,只有 p 才走 handler。
最常用的拦截:get 和 set 怎么写才不出错?
get 和 set 是日常开发里用得最多的两个 trap,但它们的参数和返回值有明确契约,乱写容易引发静默失败或无限递归。
-
get(target, prop, receiver):第三个参数receiver是当前 Proxy 实例(或继承链上的调用者),用于正确绑定this,比如在 getter 中调用Reflect.get(target, prop, receiver)而不是target[prop] -
set(target, prop, value, receiver):必须显式返回true表示赋值成功;返回false或抛异常会触发TypeError(严格模式下) - 避免在
get里直接读target[prop],否则可能绕过后续拦截逻辑;推荐统一用Reflect.get(...)
const obj = { x: 1 };
const p = 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);
const result = Reflect.set(target, prop, value, receiver);
if (!result) throw new Error(`赋值失败: ${prop}`);
return result; // ✅ 必须返回布尔值
}
});
for...in、Object.keys 为什么没被拦截?
因为它们底层调用的是 ownKeys + getOwnPropertyDescriptor,而不是 get。想控制属性枚举行为,必须显式实现这两个 trap:
立即学习“Java免费学习笔记(深入)”;
-
ownKeys(target)控制Object.getOwnPropertyNames、Object.keys、for...in等返回哪些 key(返回数组) -
getOwnPropertyDescriptor(target, prop)决定某个 key 的描述符是否暴露(比如是否可枚举、是否可配置) - 如果只写了
ownKeys却没配getOwnPropertyDescriptor,某些操作(如JSON.stringify)可能拿不到完整信息
例如隐藏以 _ 开头的属性:
const p = new Proxy({ a: 1, _b: 2 }, {
ownKeys(target) {
return Reflect.ownKeys(target).filter(k => !k.startsWith('_'));
},
getOwnPropertyDescriptor(target, prop) {
if (prop.startsWith('_')) return undefined;
return Reflect.getOwnPropertyDescriptor(target, prop);
}
});
Proxy 不能拦截哪些操作?
不是所有操作都能被 Proxy 拦截。几个典型漏网之鱼:
-
in操作符:不触发has?错,它会触发hastrap —— 但很多人忘了写,导致行为不符合预期 - 实例方法调用(如
arr.push()):不走get,而是走apply(仅限函数对象)或原型链查找;数组方法本身无法被单个 Proxy 拦截,除非代理其原型或重写方法 - 私有字段
#x:任何 Proxy 都无法拦截对私有字段的访问,这是语言硬性限制 -
Object.isExtensible、Object.preventExtensions等元操作:需通过isExtensible、preventExtensions等 trap 显式控制,否则默认透传
真正容易被忽略的,是 receiver 参数在嵌套代理或继承场景下的作用——它决定了 this 绑定源头,漏传可能导致 getter/setter 中的 this 指向错误目标。











