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的完美配合
- 丰富的实际应用场景
💡 学习建议:
- 深入理解各种代理拦截器
- 掌握常用代理模式
- 注意性能影响
- 合理使用代理链
- 始终做好错误处理
如果你觉得这篇文章有帮助,欢迎点赞收藏,也期待在评论区看到你的想法和建议!👇
终身学习,共同成长。
咱们下一期见
💻