《javascript设计模式与开发实践》阅读笔记(6)—— 代理模式

代理模式:是为一个对象提供一个代用品或占位符,以便控制对它的访问。

代理模式的关键是,当客户不方便直接访问一个对象或者不满足需要的时候,提供一个替身对象来控制对这个对象的访问,客户实际上访问的是替身对象。替身对象对请求做出一些处理之后,再把请求转交给本体对象。
基本可以理解为粉丝(客户),经纪人(代理),偶像(对象)。经纪人就相当于偶像的代理,需求直接提给经纪人,经纪人这边可以进行很多逻辑上的处理,比如可以帮助偶像过滤掉很多请求等等。

1.保护代理和虚拟代理

像上面那种,请求被代理拒绝掉就是保护代理。
把一些开销很大的对象,延迟到真正需要它的时候才去创建的代理,就是虚拟代理。
保护代理用于控制不同权限的对象对目标对象的访问,但在JavaScript并不容易实现保护代理,因为我们无法判断谁访问了某个对象。而虚拟代理是最常用的一种代理模式。

2.虚拟代理实现图片预加载

在Web 开发中,图片预加载是一种常用的技术,如果直接给某个img标签节点设置src属性,由于图片过大或者网络不佳,图片的位置往往有段时间会是一片空白。常见的做法是先用一张loading图片占位,然后用异步的方式加载图片,等图片加载好了再把它填充到img节点里,这种场景就很适合使用虚拟代理。

 

先随便创建一个img对象

复制代码

 1     var myImage = (function(){
 2         var imgNode = document.createElement( 'img' );    //创建节点
 3         document.body.appendChild( imgNode );
 4         return {   //闭包返回一个对象,包含可以设置节点src属性的方法
 5             setSrc: function( src ){
 6                 imgNode.src = src;
 7             }
 8         }
 9     })();
10 
11     myImage.setSrc( '真正的图片.jpg' );

复制代码

这样的代码在网速很慢时,图片位置会出现较长时间空白。现在开始引入代理对象proxyImage,通过这个代理对象,在图片被真正加载好之前,页面中将出现一张占位图loading.gif,来提示用户图片正在加载。

按 Ctrl+C 复制代码

 

按 Ctrl+C 复制代码

这里我们也可以看出,资源的加载只需要一次,只要这张图片下载好了,通过src都能很快显示它。

3.代理的意义

图片预加载功能不通过代理也可以实现,如下:

复制代码

 1     var MyImage = (function(){
 2         var imgNode = document.createElement( 'img' );  //创建节点
 3         document.body.appendChild( imgNode );
 4         var img = new Image;    //创建一个图片对象
 5         img.onload = function(){  //图片对象异步加载
 6             imgNode.src = img.src;   //节点更换src
 7         };
 8         return {
 9             setSrc: function( src ){
10                 imgNode.src = 'loading.gif';   //预先给个占位图片
11                 img.src = src;    //图片对象设置src
12             }
13         }
14     })();
15 
16     MyImage.setSrc( '真正的图片.jpg' );

复制代码

看起来没什么问题,但是我们得提到一个面向对象设计的原则——单一职责原则。
一个类(通常也包括对象和函数等),应该仅有一个引起它变化的原因。如果一个对象承担了多项职责,就意味着这个对象将变得巨大,引起它变化的原因可能会有多个。如果一个对象承担的职责过多,等于把这些职责耦合到了一起,这种耦合会导致脆弱和低内聚的设计。当变化发生时,设计可能会遭到意外的破坏,我们可能不得不繁琐的修改代码或者重写函数。
比如上段代码中的MyImage对象除了负责给img节点设置src外,还要负责预加载图片。我们在处理其中一个职责时,有可能因为其强耦合性影响另外一个职责的实现。
我们需要的只是给img 节点设置src,预加载图片只是一个锦上添花的功能。

使用代理模式的代码中,我们并没有改变或者增加MyImage的接口,但是通过代理对象,实际上给系统添加了新的行为。这是符合开放—封闭原则的。给img节点设置src和图片预加载这两个功能,被隔离在两个对象里,它们可以各自变化而不影响对方。何况就算有一天我们不再需要预加载,那么只需要改成请求本体而不是请求代理对象即可。

4.代理和本体接口的一致性

如果有一天我们不再需要预加载,那么就不再需要代理对象,可以选择直接请求本体。其中关键是代理对象和本体都对外提供了setSrc 方法。这样做有两个好处,(1)使用者不会被api搞糊涂(2)在任何使用本体的地方都可以替换成使用代理。

特别:如果代理对象和本体对象都为一个函数(函数也是对象),函数必然都能被执行,则可以认为它们也具有一致的“接口”。

复制代码

 1     var myImage = (function(){
 2         var imgNode = document.createElement( 'img' );
 3         document.body.appendChild( imgNode );
 4         return function( src ){
 5             imgNode.src = src;
 6         }
 7     })();
 8 
 9     var proxyImage = (function(){
10         var img = new Image;
11         img.onload = function(){
12             myImage( this.src );
13         }
14         return function( src ){
15             myImage( 'loading.gif' );
16             img.src = src;
17         }
18     })();
19 
20     proxyImage( '真正的图片.jpg' );

复制代码

5.虚拟代理合并HTTP请求

前面高阶函数的时候提过节流函数,这里也是一样,为了避免可能频繁触发的请求导致的服务器压力,可以设置一个时间延迟,比如说2s,2s后会把请求一起提交一次。只不过现在这里通过代理来实施。

6.缓存代理

缓存代理的例子——计算乘积

按 Ctrl+C 复制代码

 

按 Ctrl+C 复制代码

用高阶函数动态创建代理

通过传入高阶函数这种更加灵活的方式,可以为各种计算方法创建缓存代理。

 

复制代码

 1     /**************** 计算乘积 *****************/
 2     var mult = function(){
 3         var a = 1;
 4         for ( var i = 0, l = arguments.length; i < l; i++ ){
 5             a = a * arguments[i];
 6         }
 7         return a;
 8     };
 9     /**************** 计算加和 *****************/
10     var plus = function(){
11         var a = 0;
12         for ( var i = 0, l = arguments.length; i < l; i++ ){
13             a = a + arguments[i];
14         }
15         return a;
16     };
17     /**************** 创建缓存代理的工厂 *****************/
18     var createProxyFactory = function( fn ){  //这里只是把最后调用的函数变成参数传进来而已
19         var cache = {};
20         return function(){
21             var args = Array.prototype.join.call( arguments, ',' );
22             if ( args in cache ){
23                 return cache[ args ];
24             }
25             return cache[ args ] = fn.apply( this, arguments );
26         }
27     };
28 
29     var proxyMult = createProxyFactory( mult ),
30     proxyPlus = createProxyFactory( plus );
31     alert ( proxyMult( 1, 2, 3, 4 ) ); // 输出:24
32     alert ( proxyMult( 1, 2, 3, 4 ) ); // 输出:24
33     alert ( proxyPlus( 1, 2, 3, 4 ) ); // 输出:10
34     alert ( proxyPlus( 1, 2, 3, 4 ) ); // 输出:10

复制代码

 

 

总结

代理模式包括许多小分类,在JavaScript 开发中最常用的是虚拟代理和缓存代理。代理模式可以理解为一个中转站,其内部必然会对本体进行调用。我们在编写业务代码的时候,不需要去预先猜测是否需要使用代理模式。当真正发现不方便直接访问某个对象的时候,再编写代理也不迟。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值