JavaScript设计模式与开发实践---读书笔记(6) 代理模式

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

代理模式的关键是,当客户不方便直接访问一个对象或者不满足需要的时候,提供一个替身对象来控制对这个对象的访问,客户实际上访问的是替身对象。替身对象对请求做出一些处理后,再把请求转交给本体对象。

保护代理:代理B可以帮助代理A过滤掉一些请求,这种代理叫保护代理。

虚拟代理:虚拟代理把一些开销很大的对象,延迟到真正需要它的时候才去创建。虚拟代理是最常用的一种代理模式。

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

图片预加载是一种常用技术。

常见的做法是先用一张loading图片占位,然后用异步的方式加载图片,等图片加载好了再把它填充到img节点里,这种场景就很适合使用虚拟代理。

    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(){
            myImage.setSrc(this.src);
        }
        return {
            setSrc: function(src){
                myImage.setSrc('images/1.jpg');//本地图片
                img.src = src;
            }
        }
    })();

    proxyImage.setSrc('http://img04.sogoucdn.com/app/a/100520021/b173203599e22d825c5f905d1da92535');

代理的意义:
单一职责原则:就一个类(通常也包括对象和函数)而言,应该仅有一个引起它变化的原因。职责被定义为"引起变化的原因"。

实际上,我们需要的只是给img节点设置src,预加载图片只是一个锦上添花的功能。于是代理的作用在这里就体现出来了,代理负责预加载图片,预加载的操作完成之后,把请求重新交给本体MyImage。

 

代理和本体接口的一致性:

上述代码,其中的关键是代理和本体都对外提供了setSrc方法,在客户看来,代理对象和本体是一致的,代理接手请求的过程对于用户来说是透明的,用户不清楚代理和本体的区别。

这样有两个好处:

  1. 用户可以放心的请求代理,他只关心是否能得到想要的结果。
  2. 在任何使用本体的地方都可以替换成使用代理。

另外,如果代理对象和本体都为一个函数(函数也是对象),函数必然都能被执行,则可以认为它们也具有一致的"接口",代码如下:

var myImage = (function(){
        var imgNode = document.createElement('img');
        document.body.appendChild(imgNode);

        return {
                imgNode.src = src;
        }
    })();

    var proxyImage = (function(){
        var img = new Image;
        img.onload = function(){
            myImage(this.src);
        }
        return function(src){
                myImage.setSrc('images/1.jpg');//本地图片
                img.src = src;
            }
    })();

    proxyImage.setSrc('http://img04.sogoucdn.com/app/a/100520021/b173203599e22d825c5f905d1da92535');


2.虚拟代理合并HTTP请求

在Web开发中,也许最大的开销就是网络请求。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>虚拟代理合并http请求</title>
</head>
<body>
    <input type="checkbox" id="1">1
    <input type="checkbox" id="2">2
    <input type="checkbox" id="3">3
    <input type="checkbox" id="4">4
    <input type="checkbox" id="5">5
    <input type="checkbox" id="6">6
    <input type="checkbox" id="7">7
    <input type="checkbox" id="8">8
    <input type="checkbox" id="9">9
</body>
</html>
<script>
    var synchronousFile = function(id){
        console.log('开始同步文件,id为:'+id);
    };

    var proxySynchronousFile = (function(){
        var cache = [],//保存一段时间需要同步的ID
            timer;//定时器
            return function (id){
                cache.push(id);
                if(timer){    //保证不会覆盖已经启动的定时器
                    return;
                }

                timer = setTimeout(function(){
                    synchronousFile(cache.join(','));//两秒后向本体发送需要同步的ID集合
                    clearTimeout(timer);    //清空定时器
                    timer = null;
                    cache.length = 0;//清空ID集合
                },2000)
            }
    })();
    var checkbox = document.getElementByTagName('input');
    for(var i=0,c;c=checkbox[i++]; ){
        c.onclick = function(){
            if(this.checked === true){
                proxysynchronousFile(this.id);
            }
        }
    };

</script>

3.虚拟代理在惰性加载中的应用

4.缓存代理

缓存代理可以为一些开销大的运算结果提供暂时的存储,在下次计算时,如果传进来的参数跟之前一致,则可以直接返回前面存储的运算结果。

例子:1.计算乘积

   

    var mult = function(){
        console.log('开始计算乘积');
        var a = 1;
        for( var i=0,l=arguments.length;i<l;i++){
            a = a*arguments[i];
        }
        return a;
    };

    mult(2,3);
    mult(2,3,4);

    //缓存代理函数
    var proxyMult = (function(){
        var cache = {};
        return function(){
            var args = Array.prototype.join.call(arguments,',');
            if(args in cache){
                return cache[args];
            }
            return cache[args] = mult.apply(this,arguments);
        }
    })();

    proxyMult(1,2,3,4);
    proxyMult(1,2,3,4);//第二次调用proxyMult时,本体mult函数并没有被计算,proxyMult直接返回了之前缓存好的计算结果

2.缓存代理用于ajax异步请求数据

 

5.用高阶函数动态创建代理

可以为各种方法创建缓存代理。

 

var mult = function(){
        var a = 1;
        for( var i=0,l=arguments.length;i<l;i++){
            a = a*arguments[i];
        }
        return a;
    };

    var plus = function(){
        var a = 0;
        for( var i=0,l=arguments.length;i<l;i++){
            a = a+arguments[i];
        }
        return a;
    };

    var createProxyFactory = function(fn){
        var cache = {};
        return function(){
            var args = Array.prototype.join.call(arguments,',');
            if(args in cache){
                return cache[args];
            }
            return cache[args] = fn.apply(this,arguments);
        }
    };

    var proxyMult = createProxyFactory(mult),
    proxyPlus = createProxyFactory(plus);

    alert(proxyMult(1,2,3,4));
    alert(proxyMult(1,2,3,4));
    alert(proxyPlus(1,2,3,4));
    alert(proxyPlus(1,2,3,4));

 

6.其他代理模式

  • 防火墙代理:控制网络资源的访问。
  • 远程代理
  • 保护代理
  • 智能引用代理
  • 写时复制代理:通常用于复制一个庞大对象的情况。

7. 小结

JavaScript开发中最常用的是虚拟代理和缓存代理。

 

posted @ 2016-10-08 13:37  cmy1025  阅读(132)  评论(0编辑  收藏  举报