news 2026/8/24 15:08:36

设计模式之-代理模式

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
设计模式之-代理模式

1.代理实现懒加载1

varmyImage=(function(){varimg=document.createElement('img');document.body.appendChild(img);return{setSrc:function(src){img.src=src;}}})();varproxyImage=(function(){// 创建预加载图片varimg=newImage();img.onload=function(){// 预加载完成后,设置真实图片myImage.setSrc(this.src)}return{setSrc:function(src){// 先显示loading图片myImage.setSrc('./loading.png');img.src=src;}}})();// 使用示例proxyImage.setSrc('https://fastly.picsum.photos/id/784/200/300.jpg?hmac=LIWlcHgxQH79XHKNji8Jin_KakntjYyd9VXyckNYFbE')

代理实现懒加载2,class的写法

classMyImage{constructor(){this.img=document.createElement('img');document.body.appendChild(this.img);}setSrc(src){this.img.src=src;}}classProxyImage{constructor(realImage){this.img=realImage;this.cacheImage=null;// 预加载的图片对象}setSrc(src){// 先显示loading图片this.img.setSrc('./loading.png');// 创建预加载图片this.cacheImage=newImage();this.cacheImage.onload=()=>{// 预加载完成后,设置真实图片this.img.setSrc(src);};this.cacheImage.src=src;}}// 使用示例constimg=newMyImage();constproxyImg=newProxyImage(img);proxyImg.setSrc('https://fastly.picsum.photos/id/784/200/300.jpg?hmac=LIWlcHgxQH79XHKNji8Jin_KakntjYyd9VXyckNYFbE')

代理引发的思考:也许有人会有疑惑,不过是实现一个小小的图片懒加载,即使不引用任何模式也能办到,那么引入代理模式的好处究竟在哪里呢?下面我们先抛开代理,编写一个更常见的图片预加载函数,不使用代理的与加载图片函数实现如下:

varmyImage=(function(){varimgNode=document.createElement('img');document.body.appendChild(imgNode);varimg=newImage();img.onload=function(){// 预加载完成后,设置真实图片imgNode.src=img.src}return{setSrc:function(src){imgNode.src='./loading.png';img.src=src}}})();// 使用示例myImage.setSrc('https://fastly.picsum.photos/id/784/200/300.jpg?hmac=LIWlcHgxQH79XHKNji8Jin_KakntjYyd9VXyckNYFbE')

代理的意义

1.为了说明代理的意义,下面我们引入一个面向对象设计的原则–单一职责原则。
2.单一职责原则指的是,就是一个类(通常也包括对象和函数等)而言,应该仅有一个引起他变化的原因。如果一个对象承担了多项职责,就意味着这个对象将变得巨大,引起他变化的原因可能有多个。面向对象设计鼓励将行为分布到细粒度的对象当中,如果一个对象承担的职责过多,等于把这些职责耦合到了一起,这种耦合会导致脆弱和低内聚的设计,当变化发生时,设计可能会遭到意外的破坏。
3.职责被定义为“引起变化的原因”。上面代码中的MyImage对象除了负责给img节点设置src外,还要负责预加载图片。我们在处理其中一个职责的时候,有可能因为其强耦合性影响另外一个职责的实现。
4.另外,在面向对象的程序设计中,大多数情况下,若违反其他任何原则,同时将违反开放-封闭原则。如果我们只是从网络上获取一些体积很小的图片,或者5年后网速快到根本不在需要预加载了,我们可能希望把预加载图片的这段代码从MyImage对象里删掉。这时候就不得不改动MyImage对象了。
5.实际上,我们需要的只是给img节点设置src,预加载图片只是一个锦上添花的功能。如果能把这个操作放到另一个对象里面,自然是一个很好的方法。于是代理的作用就体现出来了,代理负责预加载图片,预加载的操作完成后,把请求重新交给本体MyImage.
6.纵观整个程序,我们并没有改变或者增加MyImage的接口,但是通过代理对象,实际上给系统添加了新的行为。这是符合开放-封闭原则的。给img节点设置src和图片预加载这两个功能,被隔离在两个对象里,他们可以各自变化而不影响对方。何况就算有一天我们不再需要预加载,那么只需要改成请求本体而不是请求代理对象即可。

代理和本体接口的一致性

1.如果有一天我们不再需要预加载了,那么就不在需要代理对象,可以选择直接请求本体。其中关键是代理对象和本地都对外提供了setSrc方法,在客户看来,代理对象和本体是一致的,代理接手请求的过程对于用户来说是透明的,用户并不清楚代理和本体的区别,那么这么做有两个好处
(1)用户可以放心的请求代理,他只关心是否能得到想要的结果
(2)在任何使用本体的地方都可以替换成使用代理
2.在Java等语言中,代理和本体都需要显式地实现同一个接口,一方面接口保证了他们会拥有同样的方法,另一方面,面向接口编程迎合依赖倒置原则,通过接口进行向上转型,从而避开编译器的类型检查,代理和本体将来可以被替换使用
3.在javascript这种动态类型语言中,我们有时候通过鸭子类型来检测代理和本体是否都实现setSrc方法,另外大多数时候甚至干脆不做检测,全部依赖程序员的自觉性,这对于程序的健壮性是有影响的。不过对于一门快速开发的脚本语言,这些影响还是在可以接受的范围内,而且我们也习惯了没有接口的世界。
4.另外值得一提的是,如果代理对象和本体对象都为一个函数(函数也是对象),而函数必然都能被执行,则可以认为它们具有一致的“接口”,代码如下:

varmyImage=(function(){varimg=document.createElement('img');document.body.appendChild(img);returnfunction(src){img.src=src;}})();varproxyImage=(function(){// 创建预加载图片varimg=newImage();img.onload=function(){// 预加载完成后,设置真实图片myImage(this.src)}returnfunction(src){// 先显示loading图片myImage('./loading.png');img.src=src;}})();// 使用示例proxyImage('https://fastly.picsum.photos/id/784/200/300.jpg?hmac=LIWlcHgxQH79XHKNji8Jin_KakntjYyd9VXyckNYFbE')

2.ts版本的demo

interfaceIService{operation():void;}// 提供真实服务的类classServiceimplementsIService{operation():void{console.log("Service operation called");}}classMyProxyimplementsIService{// 代理类中包含一个真实服务的引用privaterealService:Service;constructor(se:Service){this.realService=se;}// 对外界的访问进行一个过滤checkAccess():boolean{// 这边可以进行一些访问控制的操作// ...returntrue;}// 实现接口中定义的方法operation():void{// 这边需要对外界的访问进行一个过滤if(this.checkAccess()){// 经过访问控制后,可以调用真实服务的方法this.realService.operation();}}}// 使用constrealService=newService();constproxy=newMyProxy(realService);// 外界要访问服务的时候,是和代理类打交道的proxy.operation();

3.前端中的代理模式-- ES6新增的Proxy构造方法

// 创建一个要被代理的对象// 这是真实的对象consttarget={name:"John",age:30,};// 定义代理的行为// 代理对象就会对外界的访问需求进行过滤consthandler={// 拦截对象属性的读取get(target,prop,receiver){console.log(`[GET]:${prop}`);returnReflect.get(target,prop,receiver);},// 拦截对象属性的设置set(target,prop,value,receiver){console.log(`[SET]:${prop}=${value}`);returnReflect.set(target,prop,value,receiver);},};// 使用Proxy构造函数创建代理对象// new Proxy 会返回一个对象,该对象就是针对真实对象的代理对象constproxy=newProxy(target,handler);// 测试代理的行为// 之后就通过代理对象来访问真实对象的成员console.log(proxy.name);// 输出:[GET]: name 以及 Johnproxy.age=31;// 输出:[SET]: age = 31

非原创,来源渡一谢杰老师和javascript设计模式与开发实践 作者曾探,简单记录,一起学起来吧

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/23 23:54:49

【系统架构】服务器部件说明

1.11.2“服务器设备的电路板和电容”是一个非常核心的话题,这直接关系到服务器的稳定性、可靠性和性能。我们可以从两个层面来深入理解:1. 服务器主板:系统的“骨架”与“神经网络”服务器主板远比普通台式机主板复杂,是其所有硬件…

作者头像 李华
网站建设 2026/8/24 9:40:43

4.2 X-Ray 视角透视 Agent:用 Langfuse 追踪、调试与优化你的 AI 应用

X-Ray 视角透视 Agent:用 Langfuse 追踪、调试与优化你的 AI 应用 导语:在上一章,我们建立了评估体系的“理论大厦”。但理论需要工具来落地。如果说开发 AI 应用像是在造一架精密的飞机,那么没有追踪和可观测性工具,就相当于在没有仪表盘和黑匣子的情况下“盲飞”。这无疑…

作者头像 李华
网站建设 2026/8/24 5:25:14

4.5 AI 世界的“防火墙”:从零构建 LLM 攻击实时检测系统

4.5 AI 世界的“防火墙”:从零构建 LLM 攻击实时检测系统 导语:我们已经构建了功能强大、可观测、可评估的 AI Agent。但我们是否忽略了一个致命的“阿喀琉斯之踵”——安全?当你的 Agent 能够调用 API、访问数据库、甚至执行代码时,它就从一个信息处理器,变成了一个拥有“…

作者头像 李华
网站建设 2026/8/23 3:22:57

【dz-1003】基于太阳能供电的园林远程监控系统

摘要 随着园林管理现代化进程的加快,精准把控园林土壤及环境状况对提升植物成活率、降低养护成本具有重要意义。传统的园林管理模式依赖人工巡检,不仅存在响应滞后、劳动强度大的问题,还因人为判断差异导致管理标准不统一,难以满…

作者头像 李华
网站建设 2026/8/23 15:46:37

4.4 线上vs离线:使用 Langfuse 实现智能体的全方位无死角评估

线上vs离线:使用 Langfuse 实现智能体的全方位无死角评估 导语:我们已经学会了用 Langfuse 收集 AI 应用的“痕迹”(Traces)。现在,是时候从这些痕迹中“断案”了——也就是进行评估。评估是连接“可观测性”和“持续优化”的桥梁。在本章中,我们将深入 Langfuse 的核心评…

作者头像 李华