Proxy 是 JavaScript 原生提供的可编程对象代理机制,通过 new Proxy(target, handler) 为对象定义 get/set 等拦截行为;它不递归代理嵌套对象,不监听数组原型方法,也不兼容 IE,需手动处理递归、数组和新增属性等场景。
Proxy 不是 Vue 或 React 的专属功能,它是 JavaScript 原生提供的对象代理机制,允许你为任意对象定义「行为钩子」。它本身不存储数据,也不改变原对象,只是在访问、赋值、枚举等操作发生时,给你一次介入和控制的机会。
核心在于:new Proxy(target, handler) —— target 是被代理的目标对象,handler 是一个定义各种拦截方法(如 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 返回的是原始对象,不是新 Proxyget 中判断返回值是否为对象,是则递归包装:return typeof value === 'object' && value !== null ? new Proxy(value, handler) : value
或标记已代理对象proxy.newKey = 1),除非配合 has + defineProperty 控制Vue 2 用 Object.defineProperty,Vue 3 改用 Proxy,原因很实际:
Object.defineProperty 无法监听新增/删除属性(proxy.foo = 1 不触发 setter),而 Proxy 的 set 天然支持length 变更,Proxy 至少能捕获 set(但不包括 push 等原型方法调用)ownKeys、deleteProperty、apply 等),比 defineProperty 更底层、更灵活Object.defineProperty 在 IE9+ 可用真正用好 Proxy,关键是理解它不自动递归、不劫持原型方法、也不替代事件通知——它只提供钩子,后续的依赖收集、更新触发、数组补丁,全得自己补全。