JavaScript的代理(Proxy)和反射(Reflect)是ES6新增的两个核心特性,二者通常配合使用,能够实现对对象操作的拦截和自定义,为开发者提供了更灵活的对象操作能力。代理可以创建一个对象的代理实例,拦截该对象的基本操作,反射则提供了一组与代理拦截器对应的静态方法,用于执行默认的对象操作。

基础概念解析
Proxy代理
Proxy构造函数接收两个参数,第一个是要代理的目标对象,第二个是处理程序对象,处理程序对象中可以定义各种拦截器,用于拦截目标对象的操作。当对代理对象执行对应操作时,会先触发处理程序中的拦截逻辑。
// 基础Proxy使用示例
const target = {
name: 'test',
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);
console.log(proxy.name); // 输出:读取属性 name test
proxy.age = 25; // 输出:设置属性 age 为 25
Reflect反射
Reflect是一个内置的对象,它提供了一组静态方法,这些方法与Proxy的拦截器一一对应,用于执行对象的默认操作。使用Reflect的方法可以让代码更规范,同时避免一些直接操作对象可能出现的错误。
const obj = { x: 1, y: 2 };
// 使用Reflect读取属性
console.log(Reflect.get(obj, 'x')); // 输出:1
// 使用Reflect设置属性
Reflect.set(obj, 'z', 3);
console.log(obj.z); // 输出:3
// 使用Reflect判断属性是否存在
console.log(Reflect.has(obj, 'x')); // 输出:true
常用拦截器说明
Proxy的处理程序对象支持多种拦截器,以下是几个常用的拦截器及其作用:
- get(target, prop, receiver):拦截对象的属性读取操作,比如
proxy.prop、proxy['prop'] - set(target, prop, value, receiver):拦截对象的属性赋值操作,比如
proxy.prop = value - has(target, prop):拦截
in操作符,比如prop in proxy - deleteProperty(target, prop):拦截
delete操作符,比如delete proxy.prop - apply(target, thisArg, argumentsList):拦截函数调用操作,当代理目标是一个函数时生效
实战场景示例
场景一:数据校验
可以通过Proxy的set拦截器实现对象属性的类型校验,当赋值不符合要求时直接抛出错误,避免非法数据被存入对象。
// 数据校验代理
function createValidator(target, validator) {
return new Proxy(target, {
set(target, prop, value) {
if (validator[prop]) {
const isValid = validator[prop](value);
if (!isValid) {
throw new Error(`属性 ${prop} 的值 ${value} 不符合校验规则`);
}
}
return Reflect.set(target, prop, value);
}
});
}
// 定义校验规则
const userValidator = {
age: (value) => typeof value === 'number' && value >= 0 && value <= 150,
name: (value) => typeof value === 'string' && value.length > 0
};
const user = {};
const userProxy = createValidator(user, userValidator);
userProxy.name = '张三'; // 赋值成功
userProxy.age = 20; // 赋值成功
// userProxy.age = '二十'; // 抛出错误:属性 age 的值 二十 不符合校验规则
// userProxy.age = 200; // 抛出错误:属性 age 的值 200 不符合校验规则
场景二:属性访问日志
通过get拦截器可以记录所有属性访问的日志,方便调试或者统计对象属性的使用情况。
// 属性访问日志代理
function createAccessLogger(target) {
return new Proxy(target, {
get(target, prop) {
console.log(`[${new Date().toLocaleTimeString()}] 访问属性:${prop}`);
return Reflect.get(target, prop);
}
});
}
const data = {
id: 1,
title: '测试数据'
};
const dataProxy = createAccessLogger(data);
console.log(dataProxy.id);
// 输出:
// [14:30:00] 访问属性:id
// 1
console.log(dataProxy.title);
// 输出:
// [14:30:01] 访问属性:title
// 测试数据
场景三:数组变化监听
数组的push、pop等操作本质上是修改数组的length属性和对应索引的值,通过Proxy可以拦截这些操作,实现数组变化的监听。
// 数组变化监听代理
function createArrayWatcher(arr, callback) {
return new Proxy(arr, {
set(target, prop, value) {
const oldValue = target[prop];
const result = Reflect.set(target, prop, value);
if (result) {
callback({
prop,
oldValue,
newValue: value
});
}
return result;
}
});
}
const list = [];
const listProxy = createArrayWatcher(list, (change) => {
console.log('数组发生变化:', change);
});
listProxy.push(1);
// 输出:数组发生变化: {prop: '0', oldValue: undefined, newValue: 1}
// 输出:数组发生变化: {prop: 'length', oldValue: 0, newValue: 1}
listProxy[1] = 2;
// 输出:数组发生变化: {prop: '1', oldValue: undefined, newValue: 2}
// 输出:数组发生变化: {prop: 'length', oldValue: 1, newValue: 2}
注意事项
使用代理与反射API时需要注意以下几点:
- 代理只能拦截对代理对象的操作,直接操作目标对象不会触发拦截逻辑
- Reflect的方法返回值大多是布尔类型,表示操作是否成功,比直接操作对象更符合函数式编程的习惯
- 不要在拦截器中无限递归调用自身,比如get拦截器中直接返回
proxy[prop]会导致栈溢出 - 代理对象的类型和目标对象的类型不同,
proxy instanceof Object返回true,但proxy === target返回false
JavaScriptProxyReflect代理模式反射API修改时间:2026-07-01 03:03:20