ES6 的 Proxy
对象用于创建一个对象的代理,从而拦截并自定义对象的基本操作(如属性查找、赋值、枚举等)。它是 JavaScript 元编程(meta-programming)的核心工具之一。以下是详细使用方法:
1. 基本语法
const proxy = new Proxy(target, handler);
target
: 被代理的目标对象(可以是任何类型)handler
: 包含拦截操作(traps)的对象,定义代理行为
2. 常用拦截器(Traps)
(1) get
:拦截属性读取
const handler = {
get(target, prop) {
return prop in target ? target[prop] : '默认值';
}
};
const obj = { name: 'John' };
const proxy = new Proxy(obj, handler);
console.log(proxy.name); // 'John'
console.log(proxy.age); // '默认值'
(2) set
:拦截属性赋值
const handler = {
set(target, prop, value) {
if (prop === 'age' && typeof value !== 'number') {
throw new TypeError('Age must be a number!');
}
target[prop] = value;
return true; // 表示成功
}
};
const user = {};
const proxy = new Proxy(user, handler);
proxy.age = 30; // 成功
proxy.age = 'old'; // 抛出 TypeError
(3) has
:拦截 in
操作符
const handler = {
has(target, prop) {
if (prop.startsWith('_')) {
return false; // 隐藏私有属性
}
return prop in target;
}
};
const obj = { _secret: '123', public: 'data' };
const proxy = new Proxy(obj, handler);
console.log('_secret' in proxy); // false
console.log('public' in proxy); // true
(4) deleteProperty
:拦截 delete
操作
const handler = {
deleteProperty(target, prop) {
if (prop.startsWith('_')) {
throw new Error('Cannot delete private properties!');
}
delete target[prop];
return true;
}
};
const obj = { _id: 1, name: 'Alice' };
const proxy = new Proxy(obj, handler);
delete proxy.name; // 成功
delete proxy._id; // 抛出错误
(5) apply
:拦截函数调用
const handler = {
apply(target, thisArg, args) {
console.log(`Called with args: ${args}`);
return target(...args) * 2; // 修改返回值
}
};
function sum(a, b) { return a + b; }
const proxy = new Proxy(sum, handler);
console.log(proxy(2, 3)); // 输出日志,返回 10
(6) construct
:拦截 new
操作
const handler = {
construct(target, args) {
console.log(`Creating instance with args: ${args}`);
return new target(...args); // 必须返回对象
}
};
class Person {
constructor(name) { this.name = name; }
}
const ProxyPerson = new Proxy(Person, handler);
const p = new ProxyPerson('Bob'); // 输出日志
3. 其他拦截器
拦截器 | 触发操作 |
---|---|
getPrototypeOf | Object.getPrototypeOf(proxy) |
setPrototypeOf | Object.setPrototypeOf(proxy, proto) |
isExtensible | Object.isExtensible(proxy) |
preventExtensions | Object.preventExtensions(proxy) |
getOwnPropertyDescriptor | Object.getOwnPropertyDescriptor(proxy, prop) |
defineProperty | Object.defineProperty(proxy, prop, desc) |
ownKeys | Object.keys(proxy) , Object.getOwnPropertyNames(proxy) |
4. 使用场景
- 数据验证:拦截属性赋值并验证
- 格式化/转换:自动转换输入值(如字符串转数字)
- 观察者模式:监听对象变化
- API 封装:保护敏感操作或属性
- 性能优化:延迟加载对象属性
5. 可撤销代理
使用 Proxy.revocable()
创建可取消的代理:
const { proxy, revoke } = Proxy.revocable({}, handler);
proxy.name = 'Test'; // 正常操作
revoke(); // 撤销代理
proxy.name; // TypeError: Cannot perform 'get' on a revoked proxy
6. 注意事项
- 代理不是深拷贝:修改代理会影响原始对象
- 目标对象不可变:
handler
无法修改target
的引用 - 性能开销:代理操作比直接访问略慢(在性能关键代码中慎用)
- 透明性:
proxy instanceof TargetClass
仍返回true
完整示例:数据验证代理
const validator = {
set(target, prop, value) {
if (prop === 'age') {
if (typeof value !== 'number' || value < 0) {
throw new TypeError('Invalid age value');
}
}
target[prop] = value;
return true;
}
};
const person = {};
const proxy = new Proxy(person, validator);
proxy.age = 25; // 成功
proxy.age = -5; // 抛出错误
通过灵活使用 Proxy
,可以实现高度自定义的对象行为控制,为复杂逻辑提供优雅解决方案。