写在前面
最近在学习《JavaScript设计模式与开发实践》一书,每看完一个模块,会写一篇文档来做为笔记,如果你也在学习JS设计模式,欢迎留言交流。
原理
代理模式,顾名思义就是给一样东西添加一层代理,代理模式的关键是,当客户不方便直接访问一个对象或者不满足需要的时候,提供一个替身
对象来控制对这个对象的访问,客户实际上访问的是替身对象。替身对象对请求做出一些处理之
后,再把请求转交给本体对象。
另一种情况是对象需要做的事情过多,违反了单一职能原则,这时就需要考虑创建其代理对象来进行功能的拆分。
一,保护代理
假设小明给mm表白,通过一个朋友,送一束花,但是送的时机需要朋友确认mm心情,送的动作也需要朋友完成,不然花就会浪费掉,这里的朋友就是一个保护代理。
我们先来编写小明的对象
var Flower = function(){};
var xiaoming = {
sendFlower: function( target){
var flower = new Flower();
target.receiveFlower( flower );
}
};
xiaoming.sendFlower( B );
复制代码
然后再编写代理对象和MM
var proxy = {
receiveFlower: function( flower ){
MM.listenGoodMood(function(){ // 监听 MM 的好心情
var flower = new Flower(); // 延迟创建 flower 对象
MM.receiveFlower( flower );
});
}
};
var MM = {
receiveFlower: function( flower ){
console.log( '收到花 ' + flower );
},
// 假设 10 秒之后 A 的心情变好
listenGoodMood: function( fn ){
setTimeout(function(){
fn();
}, 10000 );
}
};
复制代码
最后调用xiaoming.sendFlower( proxy )就可以送出花了
二,虚拟代理
虚拟代理一般是给目标对象设置一层代理,然后通过代理完成一部分操作,用来节约资源或者方便使用。
1,图片预加载
在 Web 开发中,图片预加载是一种常用的技术,如果直接给某个 img 标签节点设置 src 属性,
由于图片过大或者网络不佳,图片的位置往往有段时间会是一片空白。常见的做法是先用一张
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( 'file:// /C:/Users/svenzeng/Desktop/loading.gif' );
img.src = src;
}
}
})();
proxyImage.setSrc( 'http:// imgcache.qq.com/music/photo/k/000GGDys0yA0Nk.jpg' );
复制代码
2,合并http请求
假设页面中dom的每一次点击都需要发送一次http请求,这会非常消耗性能,于是,我们可以通过代理将一段时间内的http请求收集起来,然后统一发送。
首先编写发送请求的组件
var checkbox = document.getElementsByTagName( 'input' );
for ( var i = 0, c; c = checkbox[ i++ ]; ){
c.onclick = function(){
if ( this.checked === true ){
proxySynchronousFile( this.id );
}
}
};
复制代码
编写统一发送的代理
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( ',' ) ); // 2 秒后向本体发送需要同步的 ID 集合
clearTimeout( timer ); // 清空定时器
timer = null;
cache.length = 0; // 清空 ID 集合
}, 2000 );
}
})();
复制代码
3,惰性加载
假如我们想要在页面加载一个模态框,但是如果用户不需要的话,直接加载会耗费一定的资源。所以我们会在用户使用其功能时加载模态框,但是为了不影响使用,我们需要在加载出真正模态框之前提供一个代理对象给用户使用。当模态框真正加载出来后才去执行用户点击的逻辑。
三,缓存代理
缓存代理可以为一些开销大的运算结果提供暂时的存储,在下次运算时,如果传递进来的参
数跟之前一致,则可以直接返回前面存储的运算结果。
1,计算乘积
//先创建一个用于求乘积的函数:
var mul = function(x,y){
return x*y;
}
复制代码
现在加入缓存函数
var mulProxy = function(){
var ache = [];
return function(x,y){
exitArr = ache.filter(item => item.x == x && item.y == y);
if(exitArr.length>0){
console.log('通过缓存得到');
return exitArr[0].result;
}else{
console.log('通过计算得到');
var result = mul(x,y);
ache.push({
x,
y,
result
});
return result;
}
}
}
var mulProxyFunc = mulProxy();
复制代码
2,高阶函数创建动态代理
/**************** 计算乘积 *****************/
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 ) ); // 输出:24
alert ( proxyMult( 1, 2, 3, 4 ) ); // 输出:24
复制代码
最后
本文介绍了JS设计模式中的代理模式的三种情况,如果你觉得本文对你有用的话,不妨点个赞再走呀…