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

Proxy 是什么:一个可编程的拦截器
Proxy 不是 Vue 或 React 的专属功能,它是 JavaScript 原生提供的对象代理机制,允许你为任意对象定义「行为钩子」。它本身不存储数据,也不改变原对象,只是在访问、赋值、枚举等操作发生时,给你一次介入和控制的机会。
核心在于:new Proxy(target, handler) —— target 是被代理的目标对象,handler 是一个定义各种拦截方法(如 get、set)的对象。
最常用的两个拦截: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,需额外处理setPrototypeOf、defineProperty或重写数组方法
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 只拦截**对代理对象本身的访问**,不递归代理嵌套对象。这是最容易踩的坑:
立即学习“Java免费学习笔记(深入)”;
- 如果
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 种拦截操作(
ownKeys、deleteProperty、apply等),比 defineProperty 更底层、更灵活 - 兼容性代价:Proxy 不支持 IE,
Object.defineProperty在 IE9+ 可用
真正用好 Proxy,关键是理解它不自动递归、不劫持原型方法、也不替代事件通知——它只提供钩子,后续的依赖收集、更新触发、数组补丁,全得自己补全。











