Proxy 通过将对象底层操作显式暴露为可编程的 trap 钩子,在对象与外界间建立可定制拦截层;它统一拦截所有基本操作,覆盖全面且不污染原对象,但不可代理原始值和函数内部槽位。

Proxy 能增强 JavaScript 对象的控制,根本原因不是它“更强大”,而是它把原本隐式、不可拦截的对象底层操作,显式暴露为可编程的钩子(traps)——你不是在扩展对象,而是在对象和外界之间加了一层可定制的拦截层。
Proxy 的 trap 是对象操作的“中间件”
JavaScript 引擎对对象的读写、枚举、构造等行为,原本由内部方法(如 [[Get]]、[[Set]]、[[OwnPropertyKeys]])直接处理。而 Proxy 让你能用普通函数重写这些行为:
-
get(target, prop, receiver)拦截属性读取,可用于响应式依赖收集 -
set(target, prop, value, receiver)拦截赋值,能校验类型或触发更新 -
has(target, prop)拦截in操作符,实现“逻辑存在但物理不存在”的属性 -
ownKeys(target)拦截Object.keys()和for...in,可隐藏或动态生成键名
这些 trap 不是语法糖,而是直接对应 ES 规范中的抽象操作,因此覆盖全面、无遗漏。
与 Object.defineProperty 的关键区别
Object.defineProperty 只能劫持**已存在的属性**,且无法监听新增/删除属性、数组索引赋值、in、delete 等操作;Proxy 则从机制上统一拦截所有基本操作:
- 对数组索引赋值(如
arr[0] = 1)会触发settrap,而 defineProperty 无法捕获 -
delete target.prop可被deletePropertytrap 拦截并自定义逻辑 - 对不存在属性的访问(
obj.missing)也能进get,便于实现默认值或链式 fallback - 不污染原对象:所有拦截逻辑都在 handler 中,target 保持纯净
但要注意:Proxy 不能代理非对象(如原始值),也不能代理 function 的 length、name 等不可配置属性——这些属于函数对象自身的内部槽位,不在 trap 覆盖范围内。
立即学习“Java免费学习笔记(深入)”;
常见误用:忽略 receiver 和 this 绑定
在 get 和 set 中,第四个参数 receiver 很关键——它是原始操作的调用者(比如代理对象本身,或继承链上的子对象)。若不传给 Reflect.get/Reflect.set,会导致 this 指向错误或原型链失效:
const p = new Proxy({ x: 1 }, {
get(target, prop, receiver) {
console.log('get', prop);
// ✅ 正确:保持 receiver,确保 getter 中的 this 指向 receiver
return Reflect.get(target, prop, receiver);
// ❌ 错误:省略 receiver,this 会指向 target,破坏原型行为
// return target[prop];
}
});
同样,apply 和 construct trap 中也必须显式处理 thisArg 和 newTarget,否则类继承或函数调用会出问题。
性能与兼容性不是“有无”,而是“是否必要”
Proxy 在现代引擎中已高度优化,但每次属性访问都多一层函数调用开销。真实项目中要权衡:
- 仅对需要响应式、验证、日志、沙箱等场景使用,不要无差别代理整个数据树
- V8 中
Proxy的get/settrap 在 hot code 下接近原生速度,但ownKeys或definePropertytrap 开销明显更高 - IE 完全不支持,若需兼容,得用
Object.defineProperty+Array.prototype补丁组合降级,但功能必然缩水
真正容易被忽略的是:Proxy 实例一旦创建就不可撤销(revocable 是特例),且它的 === 比较永远返回 false——这意味着你不能靠引用相等来判断是否是同一代理,得靠 WeakMap 存元信息或设计可识别的标识字段。











