JavaScript 代理模式入门:用 Proxy 写出更优雅的代码
本文适合前端初学者和想巩固设计模式的开发者。通过生活化例子 + 可运行代码,轻松掌握 JS 代理模式的核心思想与实战用法。
在日常开发中,你是否遇到过这些需求?
- 想在调用某个函数前加权限校验?
- 希望自动记录用户操作日志?
- 需要缓存计算结果避免重复开销?
其实,这些问题都可以用一个经典设计模式来优雅解决 —— 代理模式(Proxy Pattern) 。
今天,我们就用最简单的方式,带你彻底搞懂它!
🤔 什么是代理模式?
代理模式 是一种结构型设计模式,它的核心思想是:
不直接访问目标对象,而是通过一个“代理对象”来间接控制对它的访问。
你可以把代理想象成“中间人”:
- 它和真实对象有相同的接口;
- 它可以在转发请求前后,加入额外逻辑;
- 真实对象无需知道代理的存在。
💡 生活中的例子:送花代理人
假设你想给朋友 xm 送花,但他不在家。于是你找了一个共同好友 xh 帮忙转交。
你 → xh(代理)→ xm(真实对象)
xh和xm都能“收花”(接口一致);xh在转交前可以说一句:“这是小明送的!”;- 你只和
xh打交道,完全不用关心xm是否在家。
这就是代理模式的精髓:解耦 + 增强。
🧩 JavaScript 如何实现代理?
方法一:使用 ES6 原生 Proxy(推荐 ✅)
// 真实对象
const xm = {
receiveFlower(name) {
console.log(`${name} 收到了花`);
}
};
// 创建代理
const xh = new Proxy(xm, {
get(target, prop) {
if (prop === 'receiveFlower') {
return (...args) => {
console.log('✨ 正在送花...');
target.receiveFlower(...args);
console.log('✅ 花已送达!');
};
}
return target[prop];
}
});
// 使用代理
xh.receiveFlower('小明');
// 输出:
// ✨ 正在送花...
// 小明 收到了花
// ✅ 花已送达!
⚠️ 注意:
Proxy拦截的是属性访问,所以要用get拦截方法调用。
方法二:手动模拟代理(兼容旧环境)
如果你需要支持不支持 Proxy 的老浏览器,也可以手动封装:
const xm = {
receiveFlower(name) {
console.log(`${name} 收到了花`);
}
};
const xh = {
receiveFlower(name) {
console.log('✨ 正在送花...');
xm.receiveFlower(name);
console.log('✅ 花已送达!');
}
};
xh.receiveFlower('小红');
虽然不够灵活,但在简单场景下完全够用。
🎯 代理模式的常见应用场景
| 场景 | 实现思路 |
|---|---|
| 🔐 权限控制 | 代理检查用户角色,再决定是否调用真实方法 |
| 📝 日志记录 | 在调用前后打印日志,用于调试或埋点 |
| 🔄 缓存代理 | 第一次计算后缓存结果,后续直接返回 |
| 🚀 懒加载 | 图片/数据首次访问时才加载,提升性能 |
| 🛡️ 校验输入 | 代理先验证参数合法性,再传给真实对象 |
✅ 总结一句话
代理模式 = 用一个“中间人”控制对真实对象的访问,在不修改原对象的前提下,增强功能、提高安全性。
🚀 动手试试!
学完不如练一练,试试完成以下小任务:
- 日志代理:写一个代理,记录每次调用函数的名称和参数;
- 缓存代理:实现一个
fibProxy,缓存斐波那契数列的计算结果; - 只读代理:用
Proxy创建一个不可修改的对象(拦截set操作)。
// 示例:只读代理
const readOnly = (obj) => new Proxy(obj, {
set() {
throw new Error('该对象是只读的!');
}
});
📚 延伸阅读
- MDN - Proxy
- 《JavaScript 设计模式与开发实践》—— 曾探