JavaScript设计模式学习笔记(四)——代理模式

153 阅读3分钟

定义:代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问。简单来说就是当要访问请求一个对象实体的之前新增了一层对象用于拦截这个请求,然后再将这个请求转交给真正的本体对象,而在拦截的时候就可以对着请求做各种操作和监控。

而代理模式的应用有很多,例如当我们在web页面加载一张图片的时候,如果图片过大或者网络不好的情况下通常都会加一张loading图,当请求的图片加载完了再将图片显示出来,这里就可以用到代理模式了。

// 用于显示图片的方法本体 
var myImage = (function(){    
     var imgNode = document.createElement('img');   
     document.body.appendChild( imgNode );    
     return { 
       setSrc: function( src ){ 
         imgNode.src = src; 
       } 
     } 
 })(); 

// 定义一个代理对象

 var proxyImage = (function(){    
   var img = new Image; 
   img.onload = function(){  // 当实际请求的图片加载完以后将图片地址set到页面的img标签上
     myImage.setSrc( this.src ); 
   } 
   return { 
     setSrc: function( src ){ 
       myImage.setSrc('loading图地址'); // 代理拦截到myImage的同名方法setSrc,设置显示loading图       
       img.src = src;      
     } 
   } 
  })(); 
  proxyImage.setSrc('实际请求图片地址'); 

使用代理模式来做图片预加载的意义在于,这么做符合面向对象的单一职责原则,就是每个对象或者函数都只负责做一件事(myImage方法就是负责单一的负责将拿到的src显示到页面上),不用考虑其他的职责,即使程序的显示逻辑变了也不会影响本来的代码。我们并没有改变或者增加 MyImage 的接口,但是通过代理对象,实际上给系统添加了新的行为。给 img 节点设置 src 和图片预加载这两个功能,被隔离在两个对象里,它们可以各自变化而不影响对方。何况就算有一天我们不再需要预加载,那么只需要改成请求本体而不是请求代理对象即可。 

还有一种常用的代理模式就是缓存代理,这种模式可以代理一些开销大的运算结果提供暂时的缓存,在下次发起样参数的运算时,拦截这个运算并从缓存中取出之前的运算结果即可。

// 一个复杂的高开销的算法函数(自行脑补)
var count = function(){
    var num = 0
    for (var i = 0; i < arguments.length; i++) {
        num = num + arguments[i]
    }
    console.log('结果:' + num)
    return num
}

// 定义缓存代理函数
var proxyCount = (function(){
    var cache = {} // 闭包实现缓存对象
    return function () {
        var argsStr =  Array.prototype.join.call(arguments, ',')
        if (argsStr in cache) {
            return cache[argsStr]
        }
        return cache[argsStr] = count.apply(this, arguments) // 首次计算后将结果set到缓存对象中
    }
})()

proxyCount(1,2) // 3,首次运算
proxyCount(1,2) // 3,缓存中取的值

同样的,程序也符合单一职责原则,算法函数只负责实现计算,缓存功能由代理对象来实现

小结: 这两种代理模式是平常开发中比较常见的情况,代理模式能做到拦截访问本体对象的一些请求,并对其进行监控过滤和操作,使得本体对象只需要关心自己的“职责”就行,而且用户并不清楚代理和本体的区别,只管对代理发起请求即可。