Proxy 用于全面拦截对象操作,Reflect 提供标准操作方法;Proxy 可捕获新增/删除属性、in 操作等 13 种行为,Object.defineProperty 则不能;Reflect 应在 Proxy trap 中调用以确保原型链、私有字段和 receiver 正确性。

代理(Proxy)和反射(Reflect)是 JavaScript 中一对协作的底层机制:前者用于拦截并自定义对象操作,后者提供一套与 Proxy 拦截器一一对应的、更规范的对象操作方法。它们不是语法糖,而是运行时可控性的基础设施。
什么时候该用 Proxy 而不是 Object.defineProperty
Object.defineProperty 只能监听已存在的属性读写,无法捕获新增属性、删除属性、in 操作、for...in 遍历等行为;而 Proxy 可以拦截 13 种操作,覆盖更全。
- 需要监听对象动态增删属性(比如响应式框架中
obj.newProp = 1)→ 必须用Proxy - 想拦截
delete obj.x或obj instanceof SomeClass→Proxy提供deleteProperty和getPrototypeOf等 trap - 要让数组索引赋值触发更新(如
arr[0] = 'x')→Object.defineProperty对数组索引无效,Proxy的set可捕获 - 注意:
Proxy不能代理非对象(如原始值),也不能直接代理undefined或null
Reflect 不是可选的“工具库”,它是 Proxy 拦截器的默认行为实现
每个 Proxy 的 trap(如 get、set)里,推荐用 Reflect.get()、Reflect.set() 等调用原生逻辑,而不是手写 target[prop] —— 因为后者不支持原型链查找、不触发其他 trap、也不兼容私有字段(#field)。
const handler = {
get(target, prop, receiver) {
console.log(`读取 ${prop}`);
// ✅ 正确:走标准语义,支持原型、私有字段、receiver 绑定
return Reflect.get(target, prop, receiver);
// ❌ 错误:绕过原型,忽略 receiver,对 #field 报错
// return target[prop];
}
};
-
Reflect方法全部返回布尔值或结果值,不会抛异常(如Reflect.deleteProperty()返回true/false,而非抛错) -
Reflect.construct()是唯一能指定new.target的方式,用于子类化内置构造器(如继承Array) - 所有
Reflect方法的第一个参数都是目标对象,第二个是属性名,参数顺序统一,比对应全局函数(如Object.getOwnPropertyDescriptor)更一致
典型实用场景:校验、日志、响应式、Mock 和不可变封装
这些不是“玩具示例”,而是真实工程中高频落地的模式。
立即学习“Java免费学习笔记(深入)”;
-
运行时属性校验:在
settrap 中检查类型/范围,throw或自动转换 -
细粒度访问日志:记录谁在何时读了哪个属性(配合
console.trace()或性能标记) -
响应式系统基础:Vue 3 的
reactive()就基于Proxy+WeakMap缓存依赖 -
安全沙箱:拦截
eval、Function构造、document.write等危险操作 -
不可变包装:
settrap 直接throw,deleteProperty返回false,让对象“看起来”只读
注意:Proxy 创建的是新对象,原对象不受影响;且一旦代理,就无法撤销(ECMAScript 没有 Proxy.revoke 的通用实现,仅 revocable 特殊形式支持一次撤销)。










