javascript设计模式之三——代理模式

代理模式

代理模式:为一个对象提供一个代用品或占位符,以便控制对它的访问。
代理模式是比较有用途的一种模式,而且变种较多( 虚拟代理、远程代理、copy-on-write代理、保护代理、Cache代理、防火墙代理、同步代理、智能指引 ),应用场合覆盖从小结构到整个系统的大结构,我们也许有代理服务器等概念,代理概念可以解释为:在出发点到目的地之间有一道中间层,意为代理。
应用场景:
远程代理:也就是为了一个对象在不同的地址空间提供局部代表,这样可以隐藏一个对象存在于不同地址空间的事实,就像web service里的代理类一样。
虚拟代理:根据需要创建开销很大的对象,通过它来存放实例化需要很长时间的真实对象,比如浏览器的渲染的时候先显示问题,而图片可以慢慢显示(就是通过虚拟代理代替了真实的图片,此时虚拟代理保存了真实图片的路径和尺寸。
代码实现:

// 图片加载函数
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/image1.jpg");
            img.src = src;
        }
    }
})();

// 调用代理对象加载图片
proxyImage.setSrc( "http://images/qq.jpg");

下面是代理模式实现多图片加载的例子:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
</head>
<body>
<button id='btnLoadImg'>加载图片</button>
<br>
<div id='imgContainer'>
</div>
<br>

<script type='text/javascript' src="js/jquery.js"></script>
<script type='text/javascript'>
//图片地址保存在imgSrcs中
    var imgSrcs = [
        'http://www.carsceneuk.com/wp-content/uploads/2015/03/88y9989.jpg',
        'http://mfiles.sohu.com/20130223/5ff_403b2e7a_7a1f_7f24_66eb_79e3f27d58cf_1.jpg',
        'http://img1.imgtn.bdimg.com/it/u=2678963350,1378052193&fm=21&gp=0.jpg'
    ];

//页面加载完后开始运行 按钮绑定点击事件,一张一张加载图片
    $(document).ready(function(){
        $('#btnLoadImg').bind('click', function(){
            doLoadImgs(imgSrcs);
        });
    });

    //创建img标签
    //这里用自执行函数加一个闭包,是为了可以创建多个id不同的img标签。
    var createImgElement = (function(){
        var index = 0;

        return function() {
            var eleImg = document.createElement('img');
            eleImg.setAttribute('width', '200');
            eleImg.setAttribute('heght', '150');
            eleImg.setAttribute('id', 'img' + index++);
            return eleImg;
        };
    })();

    function loadImg(img, src) {
        img.src = src;
    }

    function createLoadImgProxy(){
        var imgCache = new Image();
        var dfd = $.Deferred();
        var timeoutTimer;

        //开始加载超时监控,超时后进行reject操作
        function beginTimeoutWatcher(){
            timeoutTimer = setTimeout(function(){
                dfd.reject('timeout');
            }, 10000);
        }

        //结束加载超时监控
        function endTimeoutWatcher(){
            if(!timeoutTimer){
                return;
            }

            clearTimeout(timeoutTimer);
        }

        //加载完成事件处理,加载完成后进行resolve操作
        imgCache.onload = function(){
            dfd.resolve(this.src);
        };

        //加载终止事件处理,终止后进行reject操作
        imgCache.onabort = function(){
            dfd.reject("aborted");
        };

        //加载异常事件处理,异常后进行reject操作
        imgCache.onerror = function(){
            dfd.reject("error");
        };

        return function(eleImg, src){

            dfd.always(function(){
//                        alert('always end');
                //加载完成或加载失败都要终止加载超时监控
                endTimeoutWatcher();
            }).done(function(src){
//                        alert('done end');
                //加载完成后,往图片元素上设置图片
                loadImg(eleImg, src);
            }).fail(function(msg){
//                        alert('fail end:' + msg);
                //加载失败后,往图片元素上设置失败图片
                loadImg(eleImg, 'images/image1.jpg');
            });

            loadImg(eleImg, 'loading.gif');
            imgCache.src = src;

            //开始进行超时加载监控
            beginTimeoutWatcher();

            return dfd.promise();
        };
    }

    //一张一张的连续加载图片
    //参数:
    //  srcs: 图片路径数组
    function doLoadImgs(srcs){
        var index = 0;

        (function loadOneByOne(){
            //退出条件
            if(!(s = srcs[index++])) {
                return;
            }

            var eleImg = createImgElement();
            document.getElementById('imgContainer').appendChild(eleImg);

            //创建一个加载代理函数
            var loadImgProxy = createLoadImgProxy();

            //在当前图片加载或失败后,递归调用,加载下一张
            loadImgProxy(eleImg, s).always(loadOneByOne);
        })();
    }
</script>
</body>
</html>

应用2:虚拟代理合并http请求

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
</head>
<body>
    <input type="checkbox" id="1"></input>1
    <input type="checkbox" id="2"></input>2
    <input type="checkbox" id="3"></input>3
    <input type="checkbox" id="4"></input>4
    <input type="checkbox" id="5"></input>5
    <input type="checkbox" id="6"></input>6
    <input type="checkbox" id="7"></input>7
    <input type="checkbox" id="8"></input>8
    <input type="checkbox" id="9"></input>9

<script type='text/javascript' src="js/jquery.js"></script>
<script type='text/javascript'>

    var synchronousFile = function( id ){
        console.log( '开始同步文件,id为: ' + id );
    };


    var proxySynchronousFile = (function(){
        var cache = [],
        timer;
        return function(id){
            cache.push(id);
            if(timer){
                return;
            }
            timer = setTimeout(function(){
                synchronousFile(cache.join(','));
                clearTimeout(timer);
                timer = null;
                cache.length = 0; // 清空ID集合
            },20000);


        }
    })();

    var checkbox = document.getElementsByTagName( 'input' );
    for ( var i = 0, c; c = checkbox[ i++ ]; ){
        c.onclick = function(){
            if ( this.checked === true ){
                proxySynchronousFile( this.id );
            }
        }
    };

</script>
</body>
</html>

将Http请求收集一段时间,最后一次性发送给服务器,减少了频繁向服务器提出请求的次数,大大减轻了服务器的压力,改善了性能。
缓存代理:缓存代理可以为一些开销大的运算结果提供暂时的存储,在下次运算时,如果传递进来的参数跟之前一致,则可以直接返回前面存储的运算结果。
代码实现:
在不使用缓存代理时,代码可能会这样写:

var mult = function(){
var a = 1;
for ( var i = 0, l = arguments.length; i < l; i++ ){
a = a * arguments[i];
}
return a;
};
mult( 2, 3 ); // 输出:6
mult( 2, 3, 4 ); // 输出:24

这样写虽然实现了基本的功能,但是存在的问题是:如果多次计算mult( 2, 3 )时,会多次调用该函数,增大了运算的开销,我们应该将第一次运算的结果保存,当第二次运算同样的数据时,应该直接返回存储的结果。
将上述代码用代理模式设计后,代码如下:

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

    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 );
        }
    })();

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

用代理模式设计代码后,运行时,可以发现console.log( ‘开始计算乘积’ );只打印了一次,proxyMult( 1, 2, 3, 4 )的结果出现两次。第二次是从缓存中获得。
安全代理:用来控制真实对象访问时的权限,一般用于对象应该有不同的访问权限。
智能指引:只当调用真实的对象时,代理处理另外一些事情。例如C#里的垃圾回收,使用对象的时候会有引用次数,如果对象没有引用了,GC就可以回收它了。
防火墙代理:控制网络资源的访问,保护主题不让“坏人”接近。
代理模式种类很多,这里只介绍了javascript中运用较多的虚拟代理和缓存代理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值