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