在 JavaScript 中,Proxy 是一种强大的工具,它允许你通过拦截对象的基本操作(如属性访问、赋值、函数调用等)来定义自定义行为。Proxy 是在 ECMAScript 6(ES6)中引入的,主要用于增强对象的功能和行为。
基本语法
const proxy = new Proxy(target, handler);
target
:需要代理的目标对象(可以是普通对象、数组或另一个 Proxy)。handler
:一个对象,包含特定操作的拦截方法(称为“trap”)。
常用 handler
的拦截方法(Trap)
以下是 Proxy 支持的常用 trap 方法,以及它们的用途:
Trap | 用途 |
---|---|
get | 拦截属性读取,例如 proxy.prop 。 |
set | 拦截属性赋值,例如 proxy.prop = value 。 |
has | 拦截属性检查,例如 'prop' in proxy 。 |
deleteProperty | 拦截属性删除,例如 delete proxy.prop 。 |
ownKeys | 拦截对象自身属性的枚举,例如 Object.keys(proxy) 或 for...in 循环。 |
apply | 拦截函数调用,例如 proxy() 。 |
construct | 拦截构造函数调用,例如 new proxy() 。 |
常见用法示例
1. 属性读取拦截
const person = {name: "Alice",age: 25
};const proxy = new Proxy(person, {get(target, prop) {if (prop in target) {return target[prop];} else {return `Property "${prop}" does not exist.`;}}
});console.log(proxy.name); // 输出: Alice
console.log(proxy.gender); // 输出: Property "gender" does not exist.
2. 属性赋值拦截(数据验证)
const person = {name: "Alice",age: 25
};const proxy = new Proxy(person, {set(target, prop, value) {if (prop === "age" && typeof value !== "number") {throw new TypeError("Age must be a number.");}target[prop] = value;return true; // 必须返回 true,表示成功}
});proxy.age = 30; // 正常赋值
console.log(proxy.age); // 输出: 30proxy.age = "thirty"; // 抛出错误: Age must be a number
3. 日志记录
const person = {name: "Alice",age: 25
};const proxy = new Proxy(person, {get(target, prop) {console.log(`Getting property "${prop}"`);return target[prop];},set(target, prop, value) {console.log(`Setting property "${prop}" to "${value}"`);target[prop] = value;return true;}
});console.log(proxy.name); // 输出日志并获取值
proxy.age = 26; // 输出日志并更新值
4. 禁止删除属性
const person = {name: "Alice",age: 25
};const proxy = new Proxy(person, {deleteProperty(target, prop) {if (prop === "name") {throw new Error("Cannot delete 'name'");}delete target[prop];return true;}
});delete proxy.age; // 成功
console.log(proxy); // 输出: { name: 'Alice' }delete proxy.name; // 抛出错误: Cannot delete 'name'
5. 拦截数组操作
const numbers = [1, 2, 3];const proxy = new Proxy(numbers, {get(target, prop) {if (prop === "last") {return target[target.length - 1];}return target[prop];},set(target, prop, value) {if (prop === "length" && value < target.length) {throw new Error("Cannot shrink the array.");}target[prop] = value;return true;}
});console.log(proxy.last); // 输出: 3
proxy.push(4); // 正常操作
console.log(proxy); // 输出: [1, 2, 3, 4]proxy.length = 2; // 抛出错误: Cannot shrink the array.
6. 动态计算属性
const calculator = {factor: 2
};const proxy = new Proxy(calculator, {get(target, prop) {if (prop === "double") {return target.factor * 2;}return target[prop];}
});console.log(proxy.factor); // 输出: 2
console.log(proxy.double); // 输出: 4
总结
Proxy 提供了一种灵活的方式来拦截和自定义对象的行为,适用于以下场景:
- 数据验证与保护
- 动态属性计算
- 日志记录或调试
- 实现代理模式或权限控制
通过 Proxy
,可以让代码更具可读性和功能性,同时也需要注意避免过度复杂的拦截逻辑。