Proxy 和 Reflect
约 1150 字大约 4 分钟
2025-03-12
ES6 的 Proxy 和 Reflect 是 JavaScript 中用于增强对象操作和元编程(Metaprogramming)的重要工具,它们通常结合使用,提供对对象行为的精细控制和统一的操作接口。
总结
- Proxy:用于拦截和自定义对象的基本操作(如属性读写、函数调用),实现元编程。
- Reflect:提供统一的对象操作方法,与 Proxy 的陷阱方法一一对应,简化默认行为的调用。
- 协同优势:Proxy 负责拦截行为,Reflect 负责执行默认操作,两者结合使代码更简洁、可维护性更高。
通过 Proxy 和 Reflect,开发者可以以声明式的方式控制对象的行为,这在框架开发、复杂系统设计等领域有广泛应用。
一、Proxy(代理)
Proxy 用于创建一个对象的代理(拦截层),可以拦截并自定义对象的基本操作(如属性读取、赋值、函数调用等)。通过代理,开发者可以监听或修改对象的默认行为,实现更复杂的逻辑。
1、核心概念
- 目标对象(Target):被代理的原始对象。
- 处理器对象(Handler):定义拦截操作的“陷阱”(Traps)方法,如
get
、set
、apply
等。
2、常见用途
- 数据验证:拦截属性赋值,确保数据合法性。
- 日志记录:跟踪对象的访问和修改。
- 虚拟属性:动态计算属性值(如
fullName = firstName + lastName
)。 - 缓存:缓存函数调用的结果,避免重复计算。
- 权限控制:限制对敏感属性的访问。
3、示例
const target = { name: "Alice", age: 25 };
const handler = {
// 拦截属性读取操作
get(target, prop) {
return prop === "age" ? "保密" : target[prop];
},
// 拦截属性赋值操作
set(target, prop, value) {
if (prop === "age" && value < 0) {
throw new Error("年龄不能为负数!");
}
target[prop] = value;
return true; // 表示赋值成功
},
};
const proxy = new Proxy(target, handler);
console.log(proxy.age); // 输出 "保密"(拦截了读取操作)
proxy.age = -5; // 抛出错误(年龄不能为负数!)
二、Reflect(反射)
Reflect 是一个内置对象,提供了一组操作对象的方法(如 Reflect.get
、Reflect.set
等),这些方法与 Proxy 的陷阱方法一一对应。
1、设计目标
- 标准化:统一原本分散在
Object
、Function
等处的对象操作方法。 - 简化 Proxy 陷阱的默认行为:在 Proxy 的陷阱中调用
Reflect
方法,可以方便地执行默认操作。
2、核心方法
Reflect 方法 | 对应 Proxy 陷阱 | 功能 |
---|---|---|
Reflect.get() | get | 读取属性 |
Reflect.set() | set | 设置属性 |
Reflect.has() | has | 检查属性是否存在(in 操作符) |
Reflect.construct() | construct | 调用构造函数创建对象 |
Reflect.apply() | apply | 调用函数 |
3、示例
const obj = { x: 10, y: 20 };
// 使用 Reflect 读取属性
console.log(Reflect.get(obj, "x")); // 10
// 使用 Reflect 设置属性
Reflect.set(obj, "z", 30);
console.log(obj.z); // 30
4、与 Proxy 结合使用
在 Proxy 的陷阱中调用 Reflect
方法,可以保留默认行为并添加自定义逻辑:
const handler = {
get(target, prop) {
console.log(`读取属性: ${prop}`);
return Reflect.get(target, prop); // 调用默认的读取行为
},
set(target, prop, value) {
console.log(`设置属性: ${prop} = ${value}`);
return Reflect.set(target, prop, value); // 调用默认的赋值行为
},
};
const proxy = new Proxy(target, handler);
proxy.age = 5; // 设置属性: age = 5
console.log(proxy.age); // 读取属性: age
三、Proxy 和 Reflect 的协同作用
1、保持默认行为
在 Proxy 的陷阱中,通过 Reflect
调用默认操作,避免手动实现复杂逻辑。
const handler = {
set(target, prop, value) {
if (prop === "age" && value < 0) {
throw new Error("年龄无效");
}
// 调用默认的赋值行为
return Reflect.set(target, prop, value);
},
};
const proxy = new Proxy(target, handler);
console.log(proxy.age); // 5
proxy.age = -5; // 抛出错误(年龄无效)
2、统一错误处理
Reflect
方法返回布尔值表示操作是否成功(如 Reflect.set
返回 true/false
),而 Object
的某些方法(如 Object.defineProperty
)在失败时会抛出错误。这种设计使错误处理更一致。
3、元编程的基石
Proxy 和 Reflect 共同实现了对对象操作的拦截和自定义,支持高级模式如依赖注入、ORM(对象关系映射)、数据绑定等。
四、实际应用场景
数据绑定(如 Vue3 的响应式系统):使用 Proxy 拦截对象属性的读写,自动触发视图更新。
API 接口保护:拦截敏感操作(如删除属性),记录日志或阻止非法行为。
函数调用的增强:拦截函数调用(
apply
陷阱),实现性能分析或参数校验。实现不可变对象:拦截所有修改操作(
set
、deleteProperty
),抛出错误以防止修改。
更新日志
e7112
-1于