js Proxy实现
Proxy 的基本概念
Proxy 是 ES6 引入的一个功能,用于创建一个对象的代理,从而可以拦截和自定义对象的基本操作。通过 Proxy,可以拦截对象的属性访问、赋值、枚举等操作,并对其进行自定义处理。
创建 Proxy
Proxy 的构造函数接受两个参数:目标对象和处理程序对象。处理程序对象包含一系列拦截操作的钩子函数(也称为“陷阱”)。
const target = {};
const handler = {
get: function(target, prop, receiver) {
return Reflect.get(...arguments);
}
};
const proxy = new Proxy(target, handler);
常用的拦截操作
Proxy 支持多种拦截操作,以下是一些常见的陷阱函数:
get
拦截对象属性的读取操作。
const handler = {
get: function(target, prop) {
return prop in target ? target[prop] : 37;
}
};
const proxy = new Proxy({}, handler);
console.log(proxy.a); // 37
set
拦截对象属性的赋值操作。
const handler = {
set: function(target, prop, value) {
if (prop === 'age' && typeof value !== 'number') {
throw new TypeError('Age must be a number');
}
target[prop] = value;
return true;
}
};
const proxy = new Proxy({}, handler);
proxy.age = 25; // 成功
proxy.age = '25'; // 抛出 TypeError
has
拦截 in 操作符。
const handler = {
has: function(target, prop) {
return prop.startsWith('_') ? false : prop in target;
}
};
const proxy = new Proxy({ _secret: 'foo', public: 'bar' }, handler);
console.log('_secret' in proxy); // false
console.log('public' in proxy); // true
deleteProperty
拦截 delete 操作符。
const handler = {
deleteProperty: function(target, prop) {
if (prop.startsWith('_')) {
throw new TypeError('Private properties cannot be deleted');
}
delete target[prop];
return true;
}
};
const proxy = new Proxy({ _secret: 'foo', public: 'bar' }, handler);
delete proxy.public; // 成功
delete proxy._secret; // 抛出 TypeError
Reflect 的使用
Reflect 对象提供了一系列静态方法,用于执行对象操作。这些方法与 Proxy 的陷阱函数一一对应,通常用于在陷阱函数中调用默认行为。
const handler = {
get: function(target, prop, receiver) {
console.log(`Getting property ${prop}`);
return Reflect.get(target, prop, receiver);
}
};
const proxy = new Proxy({ a: 1 }, handler);
console.log(proxy.a); // 输出 "Getting property a" 然后输出 1
Proxy 的常见应用场景
数据验证
通过 set 陷阱函数,可以在属性赋值时进行数据验证。
const validator = {
set: function(target, prop, value) {
if (prop === 'age' && (typeof value !== 'number' || value < 0)) {
throw new TypeError('Age must be a positive number');
}
target[prop] = value;
return true;
}
};
const person = new Proxy({}, validator);
person.age = 25; // 成功
person.age = '25'; // 抛出 TypeError
私有属性
通过 has 和 get 陷阱函数,可以模拟私有属性。
const handler = {
has: function(target, prop) {
return prop.startsWith('_') ? false : prop in target;
},
get: function(target, prop, receiver) {
if (prop.startsWith('_')) {
throw new TypeError('Cannot access private property');
}
return Reflect.get(target, prop, receiver);
}
};
const proxy = new Proxy({ _secret: 'foo', public: 'bar' }, handler);
console.log('_secret' in proxy); // false
console.log(proxy._secret); // 抛出 TypeError
函数调用拦截
Proxy 还可以用于拦截函数的调用操作。

const handler = {
apply: function(target, thisArg, argumentsList) {
console.log(`Called with arguments: ${argumentsList}`);
return Reflect.apply(target, thisArg, argumentsList);
}
};
const proxy = new Proxy(function(a, b) { return a + b; }, handler);
console.log(proxy(1, 2)); // 输出 "Called with arguments: 1,2" 然后输出 3
注意事项
- Proxy 的陷阱函数必须返回正确的值,否则可能导致意外的行为。例如,
set陷阱函数必须返回一个布尔值。 - Proxy 的性能比直接操作对象稍差,因此在性能敏感的场景中需要谨慎使用。
- 某些操作无法被拦截,例如对象的
typeof操作或instanceof操作。






