JavaScript代理模式专题 🎭
JavaScript的Proxy提供了强大的对象代理能力,能够拦截和自定义对象的基本操作。本文将深入探讨Proxy的各种模式、应用场景和最佳实践。
代理基础 🌟
💡 小知识:代理模式允许我们创建一个对象的代理,从而可以控制对这个对象的访问。JavaScript的Proxy API提供了13种基本操作的拦截器(trap),使我们能够自定义对象的行为。
javascript">// 基础代理操作
const target = { name: '张三', age: 25 };
const handler = {
get(target, property) {
console.log(`访问属性: ${property}`);
return target[property];
},
set(target, property, value) {
console.log(`设置属性: ${property} = ${value}`);
target[property] = value;
return true;
}
};
const proxy = new Proxy(target, handler);
console.log(proxy.name); // 输出: 访问属性: name 然后是 "张三"
proxy.age = 26; // 输出: 设置属性: age = 26
Proxy拦截器详解 📋
1. 基本拦截器
javascript">class BasicTraps {
static demonstrateBasicTraps() {
const handler = {
// 属性读取拦截
get(target, prop, receiver) {
return Reflect.get(target, prop, receiver);
},
// 属性设置拦截
set(target, prop, value, receiver) {
return Reflect.set(target, prop, value, receiver);
},
// 属性删除拦截
deleteProperty(target, prop) {
return Reflect.deleteProperty(target, prop);
},
// 属性存在性检查拦截
has(target, prop) {
return Reflect.has(target, prop);
}
};
return new Proxy({}, handler);
}
}
2. 高级拦截器
javascript">class AdvancedTraps {
static demonstrateAdvancedTraps() {
const handler = {
// 对象属性枚举拦截
ownKeys(target) {
return Reflect.ownKeys(target);
},
// 属性描述符获取拦截
getOwnPropertyDescriptor(target, prop) {
return Reflect.getOwnPropertyDescriptor(target, prop);
},
// 原型获取拦截
getPrototypeOf(target) {
return Reflect.getPrototypeOf(target);
},
// 原型设置拦截
setPrototypeOf(target, proto) {
return Reflect.setPrototypeOf(target, proto);
}
};
return new Proxy({}, handler);
}
}
3. 函数和构造器拦截
javascript">class FunctionTraps {
static demonstrateFunctionTraps() {
function target(a, b) {
return a + b;
}
const handler = {
// 函数调用拦截
apply(target, thisArg, args) {
console.log(`调用函数,参数:${args}`);
return Reflect.apply(target, thisArg, args);
},
// 构造函数调用拦截
construct(target, args, newTarget) {
console.log(`构造函数调用,参数:${args}`);
return Reflect.construct(target, args, newTarget);
}
};
return new Proxy(target, handler);
}
}
常用代理模式 💼
1. 验证代理
javascript">class ValidationProxy {
static createValidator(validationRules) {
return new Proxy({}, {
set(target, property, value) {
if (validationRules[property]) {
const [isValid, message] = validationRules[property](value);
if (!isValid) {
throw new Error(`验证失败: ${property} - ${message}`);
}
}
return Reflect.set(target, property, value);
}
});
}
}
// 使用示例
const userValidator = ValidationProxy.createValidator({
age: (value) => [
Number.isInteger(value) && value >= 0 && value <= 150,
'年龄必须是0-150之间的整数'
],
email: (value) => [
/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),
'邮箱格式不正确'
]
});
2. 日志代理
javascript">class LoggingProxy {
static createLogger(target, logCallback = console.log) {
return new Proxy(target, {
get(target, property) {
logCallback(`获取属性: ${property}`);
return Reflect.get(target, property);
},
set(target, property, value) {
logCallback(`设置属性: ${property} = ${value}`);
return Reflect.set(target, property, value);
},
deleteProperty(target, property) {
logCallback(`删除属性: ${property}`);
return Reflect.deleteProperty(target, property);
}
});
}
}
// 使用示例
const user = LoggingProxy.createLogger({
name: '张三',
age: 25
});
3. 访问控制代理
javascript">class AccessControlProxy {
static createPrivateProperties(target, privateProps = []) {
return new Proxy(target, {
get(target, property) {
if (privateProps.includes(property)) {
throw new Error(`无法访问私有属性: ${property}`);
}
return Reflect.get(target, property);
},
set(target, property, value) {
if (privateProps.includes(property)) {
throw new Error(`无法修改私有属性: ${property}`);
}
return Reflect.set(target, property, value);
},
deleteProperty(target, property) {
if (privateProps.includes(property)) {
throw new Error(`无法删除私有属性: ${property}`);
}
return Reflect.deleteProperty(target, property);
}
});
}
}
4. 缓存代理
javascript">class CachingProxy {
static createCached(target, ttl = 5000) {
const cache = new Map();
return new Proxy(target, {
apply(target, thisArg, args) {
const key = JSON.stringify(args);
const now = Date.now();
if (cache.has(key)) {
const [result, timestamp] = cache.get(key);
if (now - timestamp < ttl) {
return result;
}
}
const result = Reflect.apply(target, thisArg, args);
cache.set(key, [result, now]);
return result;
}
});
}
}
// 使用示例
const expensiveOperation = CachingProxy.createCached(
(x, y) => {
console.log('执行计算...');
return x + y;
}
);
最佳实践 ⭐
- 结合Reflect API使用
javascript">// 推荐
const handler = {
get(target, prop, receiver) {
return Reflect.get(target, prop, receiver);
}
};
// 不推荐
const handler = {
get(target, prop) {
return target[prop];
}
};
- 合理使用代理链
javascript">function createProxyChain(...handlers) {
return (target) => {
return handlers.reduce((proxy, handler) => {
return new Proxy(proxy, handler);
}, target);
};
}
- 错误处理
javascript">const handler = {
get(target, prop, receiver) {
try {
return Reflect.get(target, prop, receiver);
} catch (error) {
console.error(`获取属性 ${prop} 失败:`, error);
return undefined;
}
}
};
性能考虑 ⚡
- 避免过度代理
javascript">// 不推荐
function createProxy(obj) {
return new Proxy(obj, {
get: (target, prop) => Reflect.get(target, prop) // 无意义的代理
});
}
// 推荐
function createProxy(obj) {
return obj; // 如果不需要拦截,直接返回原对象
}
- 缓存代理结果
javascript">class ProxyCache {
constructor() {
this.cache = new WeakMap();
}
createProxy(target, handler) {
if (this.cache.has(target)) {
return this.cache.get(target);
}
const proxy = new Proxy(target, handler);
this.cache.set(target, proxy);
return proxy;
}
}
- 合理使用可撤销代理
javascript">function createRevocableProxy(target, handler) {
const { proxy, revoke } = Proxy.revocable(target, handler);
// 在不需要时撤销代理
setTimeout(() => {
revoke();
}, 5000);
return proxy;
}
总结 📝
JavaScript的Proxy API提供了:
- 强大的对象操作拦截能力
- 灵活的代理模式实现方式
- 与Reflect API的完美配合
- 丰富的实际应用场景
💡 学习建议:
- 深入理解各种代理拦截器
- 掌握常用代理模式
- 注意性能影响
- 合理使用代理链
- 始终做好错误处理
如果你觉得这篇文章有帮助,欢迎点赞收藏,也期待在评论区看到你的想法和建议!👇
终身学习,共同成长。
咱们下一期见
💻