Proxy 是一种在目标对象与外界之间插入可控拦截逻辑的代理机制,通过 handler 中的 trap(如 get/set)拦截操作,需配合 Reflect 方法确保原型链行为正确,常用于响应式、权限控制等场景。

Proxy 是什么?它怎么拦截对象操作?
Proxy 不是装饰器,也不是继承机制,它是一个“代理层”——在目标对象和外界之间插入一层可控的拦截逻辑。Proxy 实例本身不持有数据,所有读写最终都流向你指定的 target,但中间可以被 handler 拦截、改写、拒绝或记录。
常见错误是以为 new Proxy(obj, {}) 就能自动响应式,其实空 handler 什么都不做;必须显式定义如 get、set 等 trap 才生效。
使用场景包括:实现响应式(Vue 3)、日志追踪、权限控制、不可变封装、验证代理、Mock 数据等。
关键点:
立即学习“Java免费学习笔记(深入)”;
-
handler中的每个方法(如get)接收至少两个参数:target和key(或property),第三个通常是receiver(常为proxy自身) - 不要直接在
get里返回target[key],而应调用Reflect.get(target, key, receiver)—— 否则会丢失原型链上的访问行为(比如hasOwnProperty或 getter) -
set必须返回布尔值,true表示赋值成功,false且严格模式下会抛TypeError
Reflect 是什么?为什么不能直接用 target[key]?
Reflect 不是类,也不是构造函数,它是一组静态方法集合,每项都与 Proxy 的 trap 一一对应(如 Reflect.get ↔ get trap),作用是把原本隐式、分散、容易出错的对象操作,统一为可编程、可拦截、可重写的函数调用。
直接写 target[key] 看似简单,但在 Proxy 中会跳过原型链上的 getter、忽略 receiver 绑定、无法被其他拦截逻辑复用。而 Reflect.get(target, key, receiver) 显式传递上下文,确保行为一致。
1、什么是店中店?店中店是全诚商多用户版的一大特色,它既是独立的个体,又具有群集功能。我们做个例子说明:假设尊贵的您现实生活中租赁了一个店面,店面空间很大,您可以把您的店面分割成很多独立空间再向别人转租,这样您可以额外获得一部分租赁费用收入,借以减少你的个人租赁费用投入,还能起到活跃销售场所的气氛,俗话说:货卖一堆吗。你租赁的店面可以完全分割成很多空间向外转租,也可以自己保留一块空间为自己销售商品
常见误用:
- 在
gettrap 中写return target[key]→ 原型上定义的get不会被触发 - 在
settrap 中写target[key] = value→ 绕过 setter、忽略receiver、无法捕获失败 - 用
Object.keys(target)替代Reflect.ownKeys(target)→ 前者不包含不可枚举属性,后者忠于实际自有属性列表
一个最小可用的响应式代理示例
下面这个例子展示如何用 Proxy + Reflect 实现基础响应式:每次读取时收集依赖,每次设置时触发更新(简化版,无依赖管理细节):
const handlers = {
get(target, key, receiver) {
console.log(`[GET] ${String(key)}`);
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
console.log(`[SET] ${String(key)} =`, value);
return Reflect.set(target, key, value, receiver);
}
};
const data = { count: 0 };
const proxy = new Proxy(data, handlers);
proxy.count++; // 输出 [GET] count → [SET] count = 1
注意:proxy.count++ 是先 get 再 set,所以你会看到两行日志。如果只监听 set,就漏掉了读取依赖关系——这也是 Vue 3 为何需要配合 track/trigger 机制的原因。
容易被忽略的陷阱:receiver、this 绑定与循环代理
最常踩的坑不是语法,而是语义。例如:
-
receiver参数必须传给Reflect方法,否则this在 getter 中指向target而非proxy,导致无法触发嵌套代理或拦截失效 - 对数组或函数对象做
Proxy时,某些操作(如Array.prototype.push)会改变length,但若没在set中处理length变更,就可能漏掉响应 - 递归代理(如深响应式)必须避免无限代理:对已代理过的对象缓存并复用,否则
proxy.proxy.proxy...会爆栈 -
in操作符走hastrap,for...in走ownKeys+getOwnPropertyDescriptor,二者行为不同,不能只实现一个
复杂点不在 API 多少,而在每种 JS 操作背后对应的 trap 是否覆盖完整。一个看似简单的“监听对象变化”,实际要对至少 13 个 trap 做合理实现才能健壮。










