JavaScript设计模式四(代理模式)

JavaScript设计模式四(代理模式)

代理模式的定义:

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

代理模式的关键点在于,客户不关心请求的是代理还是本体,它只是想获得需要的东西。

我们还是通过例子来理解代理模式

代理模式例子

背景:小S需要最小L,但是小S很羞涩,刚好小C是小L和小S的朋友,小S委托小C送花给小L。

看看代码怎么实现吧,如果不经过小C,也就是不用代理模式

var Flower = function(){};

var xiaoS = {
    sendFlower: function(target){
        target.receiveFlower(new Flower());
    }
} 

var xiaoL = {
    receiveFlower: function(flower){
        console.log('receive flower: ' + flower);
    }
}

xiaoS.sendFlower(xiaoL);

如果小S勇敢一点,到这里也就结束了,大家也不用往下面继续看了,但是她怂了啊,还是需要小C来加把火

var Flower = function(){};

var xiaoS = {
    sendFlower: function(target){
        target.receiveFlower(new Flower());
    }
} 

var xiaoC = {
    receiveFlower: function(flower) {
        xiaoL.receiveFlower(flower);
    }
}

var xiaoL = {
    receiveFlower: function(flower){
        console.log('receive flower: ' + flower);
    }
}

xiaoS.sendFlower(xiaoC);

这里看起来本质上没啥区别,如果小L就是不喜欢小S,那么这里其实就是把简单的事情搞复杂了。但是小C真的就只有传递花的作用么?想想,小C是不是可以挑一下时间来送花呢?例如等小S心情好的时候再送花

var Flower = function(){};

var xiaoS = {
    sendFlower: function(target){
        target.receiveFlower(new Flower());
    }
} 

var xiaoC = {
    receiveFlower: function(flower) {
        xiaoL.listenGoodMood(function(){
            xiaoL.receiveFlower(flower);
        });
    }
}

var xiaoL = {
    receiveFlower: function(flower){
        console.log('receive flower: ' + flower);
    },
    listenGoodMood: function(fn) {
        setTimeout(function(){
            fn();   
        }, 1000);
    }
}

xiaoS.sendFlower(xiaoC);

代理模式分类

我们知道代理小C其实还可以做一些其他的事情,比如小C可以帮小L拒绝一些事情,例如追小L的送花的人没有宝马的,或者不帅的,这种请求在小C那里就直接被拦截掉了,这种模式就叫做保护代理。

我们上面看到有一个new Flower()的动作,但是这是一个很昂贵的操作,这个动作其实可以让代理去实现,代理会选择在小L心情好的时候再去new Flower(),这样给小S也节约了成本,这种形式就叫做虚拟代理。虚拟代理把一些开销很大的对象,延迟到真正需要它的时候才去创建。

虚拟代理

虚拟代理可以实现图片的预加载、合并http请求、在惰性加载中也有应用

这里以图片预加载作为例子:

先看一下不在代理的情况

var myImage = (function(){
    var imgNode = document.createElement( 'img' );
    document.body.appendChild( imgNode );
    return { 
        setSrc: function( src ){ 
            imgNode.src = src; 
        } 
    } 
})();

myImage.setSrc( 'xxxxx.jpg' );

这个会有一个明显的问题,就是如果网络情况不好,在图片加载之前会有一段时间是空白的,

我们可以在图片加载前先弄一张loading.gif

var proxyImage = (function(){ 
    var img = new Image; 
    img.onload = function(){ 
        myImage.setSrc( this.src ); 
    };
    return { 
        setSrc: function( src ){ 
            myImage.setSrc( 'C://loading.gif' ); 
            img.src = src; 
        } 
    } 
})();

proxyImage.setSrc( 'XXXXX.jpg' );

如果大家多思考一下,可能会提出一个问题,这里的代理类,其实好像没什么必要,我不用代理也可以实现上面的需求

var MyImage = (function(){
    var imgNode = document.createElement('img');
    document.body.appChild(imgNode);
    var img = new Image;
    img.onload = function(){
        imgNode.src = img.src;
    };

    return {
        setSrc: function(src) {
            imgNode.src = 'c://loading.gif';
            img.src = src;
        }
    }
})();

MyImage.setSrc('xxx.jpg');

讲到这里就不得不说代理的意义了,先看我们上面提到的单一职责原则:

单一职责原则指的是,就一个类(通常也包括对象和函数等)而言,应该仅有一个引起它变化的原因

如果一个对象有多项责任,也就有多种导致它变换的原因,这也会导致程序的耦合。

我们利用代理,把给image设置节点和图片预加载两个功能分开了,他们的变换互相不影响,比如那一天我不需要图片预加载了,直接用以前设置节点的函数就可以了,不需要去重新修改函数,不是美滋滋么

缓存代理

缓存代理的应用也很多,例如计算乘积、ajax异步请求等

我们看一下计算乘积的例子

先看不加缓存代理:

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

// 输出:6 
// 输出:24

再看加了缓存代理:

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

// 输出:24 // 输出:24

猜你喜欢

转载自blog.csdn.net/lihangxiaoji/article/details/79911230