0%

JavaScript设计模式--代理模式

代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问。代理包含保护代理和虚拟代理,保护代理可以实现对访问对象的过滤,用于控制不同权限的对象对目标对象的访问,而虚拟代理则可以将一些开销比较大的对象放在真正需要时再去创建。

虚拟代理实现图片预加载

一定程度上可以理解为数据与表现分离的一种实践,即将图片渲染到页面的功能和下载图片的功能分开,图片下载好后改变显示的状态即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//只负责图像显示
var myImage = (function(){
var imgNode = document.createElement( 'img' );
document.body.appendChild( imgNode );
return function( src ){
imgNode.src = src;
}
})();
//负责图像的加载和替换
var proxyImage = (function(){
var img = new Image;
img.onload = function(){//运用onload接口在恰当的时机显示真正的图片
myImage( this.src );
}
return function( src ){
myImage( 'file:// /C:/Users/svenzeng/Desktop/loading.gif' );
img.src = src;
}
})();
proxyImage( 'http:// imgcache.qq.com/music// N/k/000GGDys0yA0Nk.jpg' );

虚拟代理合并HTTP请求

当我们的操作可能产生高频次的HTTP请求时,为了减小对服务器的压力,我们应该将某些相同类型的高频次请求进行一定程度上的合并,比如收集一段时间内的请求信息和并进行发送。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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 );
}
})();
var checkbox = document.getElementsByTagName( 'input' );
//下面的代码使用代理模式会更加简洁
for ( var i = 0, c; c = checkbox[ i++ ]; ){
c.onclick = function(){
if( this.checked === true ){
proxySynchronousFile( this.id );
}
}
};
/***********************/
//使用代理模式获取点击事件
document.body.addEventListener("click",proxyClickHandle,false);
function proxyClickHandle(){
var evtTarget = event.target||event.srcElement;
if(evtTarget.nodeName==="INPUT"){
proxySynchronousFile(evtTarget.id);
}
}

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

许多时候为了节省HTTP资源,我们会将某些实现特定功能的脚本或者资源在真正需要的时候才进行加载,此时我们可以将用户的操作进行缓存,同时加载脚本,待脚本加载完成后再从缓存队列中取出用户的操作依次执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
var miniConsole = (function(){
var cache = [];
var handler = function( ev ){
if ( ev.keyCode === 113 ){
var script = document.createElement( 'script' );
script.onload = function(){
for ( var i = 0, fn; fn = cache[ i++ ]; ){
fn();
}
};
script.src = 'miniConsole.js';
document.getElementsByTagName( 'head' )[0].appendChild( script );
document.body.removeEventListener( 'keydown', handler );// 只加载一次 miniConsole.js
}
};
document.body.addEventListener( 'keydown', handler, false );
return {
log: function(){
var args = arguments;
cache.push( function(){
return miniConsole.log.apply( miniConsole, args );
});
}
}
})();
miniConsole.log( 11 ); // 开始打印 log
// miniConsole.js 代码
miniConsole = {
log: function(){
// 真正代码略
console.log( Array.prototype.join.call( arguments ) );
}
};

缓存代理

缓存代理可以为一些开销大的运算节省资源,尤其是在这些运算操作会进场重复的情况下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**************** 计算乘积 *****************/
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
alert ( proxyPlus( 1, 2, 3, 4 ) ); // 输出:10
alert ( proxyPlus( 1, 2, 3, 4 ) ); // 输出:10

小结

虽然代理模式非常有用,但是模式的意义始终在于解决一类特定的问题,代理模式的使用场景其实比较明确,即不方便直接访问某个对象。只有在这样的情境下,才应当使用代理模式。