什么是代理模式?JavaScript 实现一个代理模式的示例
·
代理模式(Proxy Pattern)是一种结构型设计模式,它通过引入一个 “代理对象” 来控制对 “目标对象” 的访问。代理对象可以在不改变目标对象核心功能的前提下,增加额外的逻辑(如权限校验、缓存、日志记录、延迟加载等),从而实现对目标对象的间接访问和增强。
代理模式的核心角色
- 目标对象(Subject):定义核心业务逻辑的接口或类,是代理对象最终要访问的对象。
- 代理对象(Proxy):实现与目标对象相同的接口,内部持有目标对象的引用,负责在调用目标对象前后添加额外操作,并最终转发请求给目标对象。
JavaScript 实现示例:图片懒加载代理
以下以 “图片懒加载” 为例实现代理模式。核心需求是:当图片进入可视区域时才加载真实图片,未进入时显示占位图,代理对象负责监听滚动事件、判断可视区域,并在合适时机触发真实图片的加载。
代码实现 - javascript
// -------------- 目标对象(真实图片加载器)--------------
class RealImageLoader {
constructor(imgSrc) {
this.imgSrc = imgSrc; // 真实图片地址
this.imgElement = null; // 图片DOM元素
}
// 核心功能:加载真实图片
load() {
if (!this.imgElement) {
this.imgElement = document.createElement('img');
document.body.appendChild(this.imgElement);
}
// 加载真实图片
this.imgElement.src = this.imgSrc;
console.log(`加载真实图片:${this.imgSrc}`);
}
}
// -------------- 代理对象(图片懒加载代理)--------------
class LazyLoadProxy {
constructor(imgSrc, placeholder = 'placeholder.png') {
// 持有目标对象的引用
this.realLoader = new RealImageLoader(imgSrc);
this.placeholder = placeholder; // 占位图地址
this.imgElement = null;
this.isLoaded = false; // 是否已加载真实图片
// 初始化占位图
this.initPlaceholder();
// 绑定滚动监听(代理新增的逻辑)
this.bindScrollEvent();
}
// 初始化占位图(代理的额外操作)
initPlaceholder() {
this.imgElement = document.createElement('img');
this.imgElement.src = this.placeholder;
document.body.appendChild(this.imgElement);
console.log(`显示占位图:${this.placeholder}`);
}
// 监听滚动事件,判断是否进入可视区域(代理的额外操作)
bindScrollEvent() {
const checkVisibility = () => {
if (this.isLoaded) return; // 已加载则停止检查
const rect = this.imgElement.getBoundingClientRect();
// 判断图片是否进入可视区域(上下左右均在窗口内)
const isVisible = (
rect.top < window.innerHeight &&
rect.bottom > 0 &&
rect.left < window.innerWidth &&
rect.right > 0
);
if (isVisible) {
// 进入可视区域,调用目标对象的加载方法
this.realLoader.load();
// 替换占位图为真实图片DOM(保持页面元素一致性)
this.imgElement.parentNode.replaceChild(this.realLoader.imgElement, this.imgElement);
this.isLoaded = true;
// 卸载滚动监听(优化性能)
window.removeEventListener('scroll', checkVisibility);
}
};
// 初始检查一次,后续监听滚动
checkVisibility();
window.addEventListener('scroll', checkVisibility);
}
// 实现与目标对象相同的接口(保证调用一致性)
load() {
// 代理可直接触发加载(如手动提前加载)
this.realLoader.load();
this.isLoaded = true;
}
}
// -------------- 使用示例 --------------
// 创建代理对象(而非直接使用RealImageLoader)
const img1 = new LazyLoadProxy('https://picsum.photos/800/600?random=1');
const img2 = new LazyLoadProxy('https://picsum.photos/800/600?random=2');
const img3 = new LazyLoadProxy('https://picsum.photos/800/600?random=3');
// 手动触发加载(可选)
// img3.load();
代码说明
- 目标对象(RealImageLoader):负责核心功能 —— 加载真实图片,对外提供
load方法。 - 代理对象(LazyLoadProxy):
- 持有
RealImageLoader的实例,实现与目标对象相同的load方法(保证调用方无需区分代理和目标)。 - 新增额外逻辑:初始化占位图、监听滚动事件、判断图片是否进入可视区域,仅在合适时机调用目标对象的
load方法。
- 持有
- 优势:
- 目标对象专注于核心功能(加载图片),代理对象承担非核心逻辑(懒加载),符合 “单一职责原则”。
- 若未来需要修改懒加载逻辑(如调整可视区域判断规则),只需修改代理对象,无需改动目标对象,符合 “开闭原则”。
代理模式的常见应用场景
- 权限控制:代理判断用户权限,无权限则拒绝访问目标对象。
- 缓存代理:代理缓存目标对象的计算结果,重复请求时直接返回缓存,减少计算开销。
- 日志代理:代理记录目标对象的调用参数、返回值等日志信息。
- 延迟加载:如示例中的图片懒加载,或大型对象的按需初始化。
代理模式的核心是 “控制访问” 和 “增强功能”,通过分离核心逻辑与附加逻辑,提高代码的可维护性和扩展性。
更多推荐


所有评论(0)