javascript中的代理和反射是什么_它们用于哪些场景

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.new

    Prop = 1
    )→ 必须用 Proxy
  • 想拦截 delete obj.xobj instanceof SomeClassProxy 提供 deletePropertygetPrototypeOf 等 trap
  • 要让数组索引赋值触发更新(如 arr[0] = 'x')→ Object.defineProperty 对数组索引无效,Proxyset 可捕获
  • 注意:Proxy 不能代理非对象(如原始值),也不能直接代理 undefinednull

Reflect 不是可选的“工具库”,它是 Proxy 拦截器的默认行为实现

每个 Proxy 的 trap(如 getset)里,推荐用 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 和不可变封装

这些不是“玩具示例”,而是真实工程中高频落地的模式。

  • 运行时属性校验:在 set trap 中检查类型/范围,throw 或自动转换
  • 细粒度访问日志:记录谁在何时读了哪个属性(配合 console.trace() 或性能标记)
  • 响应式系统基础:Vue 3 的 reactive() 就基于 Proxy + WeakMap 缓存依赖
  • 安全沙箱:拦截 evalFunction 构造、document.write 等危险操作
  • 不可变包装set trap 直接 throwdeleteProperty 返回 false,让对象“看起来”只读

注意:Proxy 创建的是新对象,原对象不受影响;且一旦代理,就无法撤销(ECMAScript 没有 Proxy.revoke 的通用实现,仅 revocable 特殊形式支持一次撤销)。