JavaScript的代理模式依托于内置的Proxy对象实现,允许开发者创建一个对象的代理,从而拦截并重新定义该对象的基本操作,比如属性读取、赋值、函数调用等。这种模式非常适合用来实现统一的拦截逻辑和数据验证规则,无需修改原对象的代码就能扩展功能。

代理模式核心概念
Proxy对象接收两个参数,第一个是被代理的目标对象,第二个是处理器对象,处理器对象中包含各种捕获器,用来拦截对应的操作。常见的捕获器包括get用于拦截属性读取,set用于拦截属性赋值,has用于拦截in操作符等。
基础的使用示例如下:
// 目标对象
const target = {
name: '张三',
age: 20
};
// 处理器对象,定义拦截规则
const handler = {
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);
}
};
// 创建代理对象
const proxy = new Proxy(target, handler);
// 触发get捕获器
console.log(proxy.name); // 输出:读取属性 name 张三
// 触发set捕获器
proxy.age = 21; // 输出:设置属性 age 为 21
拦截器实战应用
拦截器可以在对象操作执行前后插入自定义逻辑,比如操作日志记录、权限校验、数据格式化等。下面实现一个接口请求参数的拦截器,在发送请求前统一处理参数格式。
// 请求参数目标对象
const requestParams = {
userId: 1001,
userName: '李四'
};
// 拦截器处理器
const requestInterceptor = {
set(target, prop, value) {
// 拦截赋值操作,自动去除字符串首尾空格
if (typeof value === 'string') {
value = value.trim();
}
// 记录参数修改日志
console.log(`请求参数 ${prop} 被修改为 ${value}`);
target[prop] = value;
return true;
},
get(target, prop) {
// 拦截读取操作,添加参数来源标记
const value = target[prop];
if (value !== undefined) {
return `【请求参数】${value}`;
}
return undefined;
}
};
const proxyParams = new Proxy(requestParams, requestInterceptor);
// 测试拦截器效果
proxyParams.userName = ' 王五 '; // 输出:请求参数 userName 被修改为 王五
console.log(proxyParams.userName); // 输出:【请求参数】王五
console.log(proxyParams.userId); // 输出:【请求参数】1001
验证器实战应用
验证器可以在对象属性赋值时校验数据的合法性,不符合规则的数据直接拒绝赋值并抛出提示,避免非法数据流入后续逻辑。下面实现一个用户信息的验证器,校验年龄和邮箱格式。
// 用户信息目标对象
const userInfo = {
age: 0,
email: ''
};
// 验证器处理器
const validator = {
set(target, prop, value) {
if (prop === 'age') {
// 校验年龄必须是1-120之间的整数
if (!Number.isInteger(value) || value < 1 || value > 120) {
throw new Error('年龄必须是1到120之间的整数');
}
}
if (prop === 'email') {
// 简单校验邮箱格式
const emailReg = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/;
if (!emailReg.test(value)) {
throw new Error('邮箱格式不正确');
}
}
target[prop] = value;
return true;
}
};
const proxyUser = new Proxy(userInfo, validator);
// 测试验证器效果
try {
proxyUser.age = 25; // 合法赋值
proxyUser.email = 'test@ipipp.com'; // 合法赋值
console.log('用户信息赋值成功');
} catch (e) {
console.log(e.message);
}
try {
proxyUser.age = 150; // 非法赋值,触发错误
} catch (e) {
console.log(e.message); // 输出:年龄必须是1到120之间的整数
}
try {
proxyUser.email = 'invalid-email'; // 非法赋值,触发错误
} catch (e) {
console.log(e.message); // 输出:邮箱格式不正确
}
拦截器与验证器结合使用
实际开发中可以将拦截器和验证器的逻辑结合,既做数据校验又做操作记录,提升代码的复用性。下面的案例将之前的拦截和验证逻辑合并到一个处理器中。
const product = {
price: 0,
stock: 0
};
const productHandler = {
get(target, prop) {
console.log(`读取商品属性 ${prop}`);
return target[prop];
},
set(target, prop, value) {
// 先执行验证逻辑
if (prop === 'price') {
if (typeof value !== 'number' || value < 0) {
throw new Error('商品价格必须是大于等于0的数字');
}
}
if (prop === 'stock') {
if (!Number.isInteger(value) || value < 0) {
throw new Error('商品库存必须是大于等于0的整数');
}
}
// 再执行拦截记录逻辑
console.log(`商品 ${prop} 被设置为 ${value}`);
target[prop] = value;
return true;
}
};
const proxyProduct = new Proxy(product, productHandler);
// 测试组合效果
proxyProduct.price = 99.9; // 输出:商品 price 被设置为 99.9
proxyProduct.stock = 100; // 输出:商品 stock 被设置为 100
console.log(proxyProduct.price); // 输出:读取商品属性 price 99.9
try {
proxyProduct.price = -10; // 触发验证错误
} catch (e) {
console.log(e.message); // 输出:商品价格必须是大于等于0的数字
}
注意事项
- Proxy代理的是对象本身,修改代理对象的属性会同步影响目标对象,除非在捕获器中手动阻止。
- 捕获器中尽量使用Reflect对应的方法执行默认操作,保证行为的一致性和正确性。
- 代理模式会增加一定的性能开销,不要在高频操作的场景下过度使用。
- 代理对象没有prototype属性,无法通过instanceof判断和目标对象的关系。
JavaScript代理模式拦截器验证器Proxy修改时间:2026-07-06 06:48:24