javascript Proxy是什么_怎样实现数据拦截?

Proxy 是 JavaScript 原生提供的可编程对象代理机制,通过 new Proxy(target, handler) 为对象定义 get/set 等拦截行为;它不递归代理嵌套对象,不监听数组原型方法,也不兼容 IE,需手动处理递归、数组和新增属性等场景。

Proxy 是什么:一个可编程的拦截器

Proxy 不是 Vue 或 React 的专属功能,它是 JavaScript 原生提供的对象代理机制,允许你为任意对象定义「行为钩子」。它本身不存储数据,也不改变原对象,只是在访问、赋值、枚举等操作发生时,给你一次介入和控制的机会。

核心在于:new Proxy(target, handler) —— target 是被代理的目标对象,handler 是一个定义各种拦截方法(如 getset)的对象。

最常用的两个拦截:get 和 set 怎么写

90% 的数据响应式场景都围绕读取(get)和赋值(set)展开。它们接收的参数固定,但容易忽略细节:

  • get(target, key, receiver) 中的 receiver 是当前 Proxy 实例,用于正确处理 this 指向(比如调用 getter 方法时)
  • set(target, key, value, receiver) 必须显式返回 true 才算设置成功;返回 false 或抛错会导致静默失败或报 TypeError
  • 对数组索引赋值(如 arr[0] = 1)、修改 length、使用 push 等方法,不会触发 set,需额外处理 setPrototypeOfdefineProperty 或重写数组方法
const obj = { a: 1 };
const proxy = new Proxy(obj, {
  get(target, key) {
    console.log(`读取 ${key}`);
    return target[key];
  },
  set(target, key, value) {
    console.log(`设置 ${key} = ${value}`);
    target[key] = value;
    return true; // 必须返回 true
  }
});

为什么直接代理对象有时不生效

Proxy 只拦截**对代理对象本身的访问**,不递归代理嵌套对象。这是最容易踩的坑:

  • 如果 obj.nested = { x: 2 },后续访问 proxy.nested.x 不会触发外层 get 的逻辑,因为 nested 返回的是原始对象,不是新 Proxy
  • 解决办法是在 get 中判断返回值是否为对象,是则递归包装:return typeof value === 'object' && value !== null ? new Proxy(value, handler) : value
  • 但要注意循环引用:JSON 序列化或深度遍历时可能爆栈,需加缓存或标记已代理对象
  • Proxy 无法拦截属性不存在时的隐式创建(如 proxy.newKey = 1),除非配合 has + defineProperty 控制

Proxy 与 Object.defineProperty 的关键区别

Vue 2 用 Object.defineProperty,Vue 3 改用 Proxy,原因很实际:

  • Object.defineProperty 无法监听新增/删除属性(proxy.foo = 1 不触发 setter),而 Proxy 的 set 天然支持
  • 无法监听数组索引赋值和 length 变更,Proxy 至少能捕获 set(但不包括 push 等原型方法调用)
  • Proxy 支持 13 种拦截操作(ownKeysdeletePropertyapply 等),比 defineProperty 更底层、更灵活
  • 兼容性代价:Proxy 不支持 IE,Object.defineProperty 在 IE9+ 可用

真正用好 Proxy,关键是理解它不自动递归、不劫持原型方法、也不替代事件通知——它只提供钩子,后续的依赖收集、更新触发、数组补丁,全得自己补全。