proxy 和 Reflect 是 javaScript 实现元编程的核心工具:Proxy 用于拦截并自定义对象操作,Reflect 提供统一的底层操作接口,二者配合可安全可靠地扩展对象行为。

javascript 的 Proxy 和 Reflect 是实现元编程(metaprogramming)的核心工具。它们让开发者能在运行时拦截并自定义对象的基本操作,比如读取属性、赋值、调用函数、判断是否存在等,从而赋予对象更灵活、更可控的行为。
Proxy:为对象装上“拦截器”
Proxy 用于创建一个代理对象,它能拦截对目标对象的各类底层操作(称为“trap”),并在执行前后插入自定义逻辑。
基本语法:
const proxy = new Proxy(target, handler);
- target:要代理的原始对象
- handler:一个配置对象,定义各种 trap 方法,如
get、set、has、apply等
常见用途:
- 数据响应式(如 vue 3 的 reactive):在
set时触发更新通知 - 访问控制:阻止读取敏感字段,或对未定义属性抛出友好错误
- 日志/调试:记录每次属性访问或修改
- 验证与转换:在赋值前校验类型、格式,或自动转换值
Reflect:统一的对象底层操作接口
Reflect 是一个内置对象,提供了一组静态方法,对应所有可被 Proxy 拦截的操作(如 Reflect.get()、Reflect.set())。它不是用来替代原生操作符(如 obj.prop),而是为了:
立即学习“Java免费学习笔记(深入)”;
- 提供可编程调用方式(比如方法名可动态传入)
- 与 Proxy 配合使用时保持语义一致和可预测性
- 替代部分已被废弃的 Object API(如
Object.defineProperty的替代方案更统一)
例如:
const obj = { a: 1 }; Reflect.get(obj, 'a'); // 1 Reflect.set(obj, 'b', 2); // true(成功返回 true) Reflect.has(obj, 'a'); // true
Proxy + Reflect:协作实现可靠元编程
单独用 Proxy 定义 trap 时,若需转发操作到原对象,直接写 target[prop] 或 target[prop] = value 可能绕过原型链、不兼容 setter、或无法正确处理 this。而 Reflect 方法天然支持这些细节,推荐配合使用。
典型写法示例(带默认行为的代理):
const target = { x: 1 }; const proxy = new Proxy(target, { get(target, prop, receiver) { console.log(`读取 ${prop}`); return Reflect.get(target, prop, receiver); }, set(target, prop, value, receiver) { console.log(`设置 ${prop} = ${value}`); return Reflect.set(target, prop, value, receiver); } });
-
receiver参数确保super、this和 getter/setter 中的绑定正确(尤其涉及继承时) - 所有 Reflect 方法都返回明确的成功状态(布尔值),便于做条件控制
- 避免手动实现容易出错的底层逻辑(如属性查找规则、原型链遍历)
实际能做什么:几个轻量但有力的例子
1. 防止意外属性写入(开发环境只读保护)
function frozenObj(obj) { return new Proxy(obj, { set() { throw new Error('禁止修改对象属性(开发模式)'); } }); }
2. 自动初始化嵌套属性(类似 Lodash 的 set)
function autoCreate(target) { return new Proxy(target, { get(target, prop) { if (!(prop in target)) target[prop] = {}; return Reflect.get(target, prop); } }); } const obj = autoCreate({}); obj.a.b.c = 123; // 不报错,自动创建 a 和 b
3. 函数调用日志与耗时统计
function logCalls(fn) { return new Proxy(fn, { apply(target, thisArg, args) { console.time(`call ${fn.name}`); const result = Reflect.apply(target, thisArg, args); console.timeEnd(`call ${fn.name}`); return result; } }); }
Proxy 和 Reflect 共同构成了 JavaScript 中最自然、最安全的元编程能力。它们不改变语言语法,却大幅拓展了对象行为的表达边界——关键不在于“能做什么”,而在于“如何让行为更可预测、更易维护、更贴近业务意图”。