Proxy 是 ES6 引入的一个强大特性,它允许你创建一个对象的代理,可以拦截和自定义对象的基本操作。
const proxy = new Proxy(target, handler);
target
:要代理的目标对象handler
:包含拦截操作(traps)的对象const handler = {
// 拦截属性读取
get(target, prop, receiver) {
console.log(`Getting ${prop}`);
return Reflect.get(...arguments);
},
// 拦截属性设置
set(target, prop, value, receiver) {
console.log(`Setting ${prop} to ${value}`);
return Reflect.set(...arguments);
},
// 拦截 in 操作符
has(target, prop) {
console.log(`Checking if ${prop} exists`);
return Reflect.has(...arguments);
},
// 拦截 delete 操作
deleteProperty(target, prop) {
console.log(`Deleting ${prop}`);
return Reflect.deleteProperty(...arguments);
},
// 拦截函数调用
apply(target, thisArg, argumentsList) {
console.log('Function called');
return Reflect.apply(...arguments);
},
// 拦截 new 操作符
construct(target, argumentsList, newTarget) {
console.log('Constructor called');
return Reflect.construct(...arguments);
}
};
const target = {
name: 'John',
age: 30
};
const proxy = new Proxy(target, {
get(target, prop) {
if (prop in target) {
return target[prop];
} else {
return `Property "${prop}" does not exist`;
}
},
set(target, prop, value) {
if (prop === 'age' && typeof value !== 'number') {
throw new TypeError('Age must be a number');
}
target[prop] = value;
return true; // 表示设置成功
}
});
console.log(proxy.name); // "John"
console.log(proxy.unknown); // "Property "unknown" does not exist"
proxy.age = 31; // OK
proxy.age = 'thirty'; // TypeError: Age must be a number
Reflect 是一个内置对象,它提供了拦截 JavaScript 操作的方法。这些方法与 Proxy handler 的方法一一对应。
Reflect.get(target, propertyKey[, receiver])
Reflect.set(target, propertyKey, value[, receiver])
Reflect.has(target, propertyKey)
Reflect.deleteProperty(target, propertyKey)
Reflect.apply(target, thisArgument, argumentsList)
Reflect.construct(target, argumentsList[, newTarget])
Reflect.ownKeys(target)
Reflect.defineProperty(target, propertyKey, attributes)
Reflect.getOwnPropertyDescriptor(target, propertyKey)
Reflect.getPrototypeOf(target)
Reflect.setPrototypeOf(target, prototype)
Reflect.isExtensible(target)
Reflect.preventExtensions(target)
Reflect.defineProperty()
返回布尔值表示是否成功,而 Object.defineProperty()
返回对象或抛出错误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
// 检查属性是否存在
console.log(Reflect.has(obj, 'y')); // true
// 删除属性
Reflect.deleteProperty(obj, 'y');
console.log('y' in obj); // false
Proxy 和 Reflect 经常一起使用,在 Proxy 的 trap 中调用 Reflect 的对应方法来实现默认行为:
const target = { /* ... */ };
const proxy = new Proxy(target, {
get(target, prop, receiver) {
console.log(`Getting ${prop}`);
// 使用 Reflect 实现默认的 get 行为
return Reflect.get(target, prop, receiver);
},
set(target, prop, value, receiver) {
console.log(`Setting ${prop} to ${value}`);
// 使用 Reflect 实现默认的 set 行为
return Reflect.set(target, prop, value, receiver);
}
});
这种模式让你可以在保持对象默认行为的同时添加自定义逻辑。
function createObservable(target, observer) {
return new Proxy(target, {
set(target, key, value, receiver) {
const oldValue = target[key];
const result = Reflect.set(target, key, value, receiver);
if (result && oldValue !== value) {
observer(key, oldValue, value);
}
return result;
}
});
}
const person = { name: 'Alice', age: 25 };
const observablePerson = createObservable(person, (key, oldValue, newValue) => {
console.log(`Property ${key} changed from ${oldValue} to ${newValue}`);
});
observablePerson.name = 'Bob'; // 控制台输出: Property name changed from Alice to Bob
observablePerson.age = 30; // 控制台输出: Property age changed from 25 to 30
Proxy 和 Reflect 为 JavaScript 提供了强大的元编程能力,使得开发者能够更灵活地控制对象行为。