保护代理与虚拟代理
代理B可以帮助A过滤掉一些请求,这种代理叫做保护代理。一些操作交给代码B去执行,代理B会在时候的时候交给A,这是代理的另一种形式,虚拟代理。虚拟代理把一些开销很大的对象,延迟到真正需要他的时候才去创建。
保护代理用于控制不同权限的对象对目标的访问,但在js并不容易实现保护代理,因为我们无法判断谁访问了某个对象。而虚拟代理是最常用的一种代理模式,这里主要研究虚拟代理。
虚拟代理实现图片预加载
在web开发中,图片预加载是一种常见的技术,如果直接给某个img标签节点设置src属性,由于图片过大或者网络不佳,图片的位置往往有段时间会是一片空白。常见的做法事先用一张loading图片占位,然后用异步的方式加载图片,等图片加载好了再把它填充到img节点里,这种场景就很适合使用虚拟代理。
下面来是实现这个虚拟代理,首先创建一个普通的本体对象,这个对象负责往页面中创建一个img标签,并且提供一个对外的setSrc接口,外界调用这个接口,便可以给该img标签设置设置src属性:
var myImage = (function () {
var imgNode = document.createElement('img');
document.body.appendChild(imgNode);
return {
setSrc: function (src) {
imgNode.src = src;
}
}
})();
myImage.setSrc('');
现在开始引入代理对象proxyImage,通过这个代理对象,在图片被真正加载好之前,页面中将出现一张占位的loading.gif来提示用户图片正在加载。代码如下:
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('loading.gif');
img.src = src;
}
}
})();
proxyImage.setSrc('');
现在我们通过proxyImage间接的访问MyImage。
proxyImage控制了客户对MyImage的访问,并且在此过程中加入一些额外的操作,比如在真正的图片加载好之前,先把img节点的src设置为一张本地的loading图片。
代理的意义
实际上,我们需要的只是给imgj节点设置src,预加载图片只是一个锦上添花的功能。如果能把这个操作放在另一个对象里,自然是一个非常好的方法。于是代理的作用在这里就体现出来了,代理负责预加载图片,预加载的操作完成之后,把请求重新交给本体MyImage。纵观整个程序,我们并没有改变或者增加MyImage的接口,但是通过代理对象,实际上给系统添加了新的行为。这是符合开放-封闭原则的。给img节点设置src的图片预加载这两个功能被隔离在两个对象里,他们可以各自变化不影响对方。何况就算有天我们不再需要预加载,只需要改成请求本体而不是请求代理对象即可。
缓存代理
缓存代理可以为一些开销大的运算结果提供暂时的存储,在下次运算时,如果传进来的参数跟之前一致,则可以直接返回前面的运算结果。
一个小例子--计算乘积
先创建一个用于求乘积的函数:
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);
现在加入缓存代理函数:
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);
当我们第二次调用proxyMult(1,2,3,4);时,本体mult函数并没有被计算,proxyMult直接返回了之前缓存好的计算结果。通过增加缓存代理的方式,mult函数可以继续专注于自身的职责--计算乘积,缓存的功能是由代理对象实现的。
缓存代理用于ajax异步请求数据
我们常常在项目中遇到分页的请求,同一页的数据理论上只需要去后台拉取一次,这些已经拉取到的数据在某个地方被缓存之后,下次再请求同一页时,便可以使用之前的数据。
显然这里也可以引入缓存代理,实现方式跟计算乘积的例子差不多,唯一不同的是,请求数据是异步操作,我们无法直接把计算结果放到代理对象的缓存中,而是需要通过回调的方式。自行实现。