Proxy 是 JavaScript 中拦截对象操作的代理层,只代理对象、不深代理、需手动实现 trap;Reflect 提供与 trap 一一对应的标准化方法,确保操作符合 JS 规范语义。

Proxy 是什么?它不是装饰器,也不是继承
Proxy 是 JavaScript 中一个“站在目标对象前面”的拦截层,它不修改原对象,也不替代原对象,而是让所有对它的操作(比如读属性、赋值、调用方法)先经过你定义的逻辑。关键在于:Proxy 本身不自动响应变化,它只提供拦截入口;真正让拦截生效的是你写在 handler 里的 trap(如 get、set)。
- 它不能代理原始值(如
42、"hello"),只能代理对象(包括数组、函数、日期等) - 它不深代理:只拦截第一层访问,
proxy.a.b中的b不会自动被拦截,除非a本身也是个 Proxy - 没有
Proxy就无法实现 Vue 3 的响应式核心——因为Object.defineProperty对新增/删除属性和数组索引变化无能为力
Reflect 是干什么的?别把它当工具库用
Reflect 不是独立功能模块,而是一组与 Proxy trap 严格一一对应的静态方法(如 Reflect.get() 对应 get trap),它的存在意义只有一个:把原本隐式执行的底层操作显式化、标准化、可复用。
- 不用
Reflect也能写settrap,但得手动写target[key] = value—— 这在处理 setter、原型链、不可写属性时容易出错 - 用
Reflect.set(target, key, value, receiver)则自动遵循 JS 规范语义(比如触发 setter、尊重receiver绑定),更安全 -
Reflect.has()比key in target更可靠:后者会受原型链上hasOwnProperty影响,前者只查自有+继承属性,行为确定
最常用的组合:get/set + Reflect 实现基础响应式
这是日常开发中最可能自己写的 Proxy 场景:监听属性读写,触发副作用(如更新视图、打印日志)。重点不是“炫技”,而是避免手写逻辑绕过 JS 原生语义。
技术上面应用了三层结构,AJAX框架,URL重写等基础的开发。并用了动软的代码生成器及数据访问类,加进了一些自己用到的小功能,算是整理了一些自己的操作类。系统设计上面说不出用什么模式,大体设计是后台分两级分类,设置好一级之后,再设置二级并选择栏目类型,如内容,列表,上传文件,新窗口等。这样就可以生成无限多个二级分类,也就是网站栏目。对于扩展性来说,如果有新的需求可以直接加一个栏目类型并新加功能操作
const obj = { count: 0 };
const proxy = new Proxy(obj, {
get(target, key, receiver) {
console.log(`[GET] ${key}`);
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
console.log(`[SET] ${key} = ${value}`);
return Reflect.set(target, key, value, receiver);
}
});
proxy.count++; // 输出 [GET] count → [SET] count = 1
- 必须传
receiver给Reflect.get/set,否则类中访问this可能丢失绑定 - trap 返回值很重要:
set必须返回布尔值表示是否成功,直接return true可能掩盖赋值失败(如目标属性不可写) - 不要在
get里无条件递归代理子属性——这会无限创建 Proxy,内存爆炸
容易忽略的坑:性能、兼容性与不可撤销性
Proxy 很强大,但不是万能胶。上线前这几个点必须确认:
立即学习“Java免费学习笔记(深入)”;
- IE 完全不支持
Proxy(包括 Edge 12–18),需要 Babel 转译或降级方案(如Object.defineProperty+ 限制使用场景) - 每个 Proxy 实例都有额外内存开销,高频创建(如循环中)会拖慢 GC;建议复用 handler 或缓存代理实例
-
Proxy一旦创建就无法关闭或撤销(ES2024 的Proxy.revocable也只能“废掉”引用,不能恢复) - 某些操作无法被拦截:比如
Object.prototype.toString.call(proxy)仍返回[object Object],proxy instanceof SomeClass也照常工作——Proxy 不改变类型识别









