代理模式
- 代理模式是为对象提供一个代用品或者占位符,以便控制对他的访问。
- 代理模式的关键是,当我们不便直接访问一个对象或不满足需要时,提供一个替身对象来控制对这个对象的访问,
- 客户实际上是访问的是替身对象。替身对象对请求做出一些处理之后,在把请求转交给本体对象。
//不使用代理模式
客户--> 本体
//使用代理模式
客户-->代理-->本体
- 概念说完了,来看一个栗子
- 第一个例子——小明追 MM 的故事
四月一个晴朗的早晨,小明遇见了他的百分百女孩,我们暂且称呼小明的女神为
A。两天之后,小明决定给 A 送一束花来表白。刚好小明打听到 A 和他有一个共同的朋友 B,于是内向的小明决定让 B 来代替自己完成送花这件事情。
- 先用代码来描述一下小明追女神的过程,先看看不用代理模式的情况:
var Folwer = function(){}
var xiaoming = {
sendFlower:function(target){
var flower = new Flower();
target.receiveFlower(flower);
}
}
var A = {
receiveFlower:function(flower){
console.log("收到了" + flower);
}
}
xiaoming.sendFlower(A);
- 接下来我们引入代理B,通过代理B给A送花
var Flower = function(){};
var xiaomimg = {
sendFlower:function(target){
var flower = new Flower();
target.receiverFlower( flower );
}
}
var B = {
receiverFlower:function(flower){
A.receiverFlower( flower );
}
}
var A = {
receiverFlower:function(flower){
console.log("收到了" + flower);
}
}
xiaoming.sendFlower(B);
至此我们就完成了最简单的一个代理模式
那么有同学就要问了,小明自己去送和B去送,看起来不是二者没有本质区别,而且还把事情搞复杂了。
的确,此处的代理模式毫无用处,它所做的只是把请求简单地转交给本体。但不管怎样,我
们开始引入了代理,这是一个不错的起点。
- 那么现在我们改变故事的背景设定,假设当 A 在心情好的时候收到花,小明表白成功的几率有60%,而当 A 在心情差的时候收到花,小明表白的成功率无限趋近于 0。小明跟 A 刚刚认识两天,还无法辨别 A 什么时候心情好。如果不合时宜地把花送给 A,花被直接扔掉的可能性很大,这束花可是小明吃了 7 天泡面换来的。但是 A 的朋友 B 却很了解 A,所以小明只管把花交给 B, B 会监听 A 的心情变化,然后选择 A 心情好的时候把花转交给 A这么看下来是不是就感觉好多了
var Flower = function(){};
var xiaoming = {
sendFlower:function(target){
var flower = new Flower();
target.receiveFlower(flower);
}
}
var B = {
receiveFlower:function(flower){
A.listenGoodMood(function(){//监听A的心情
A.reveiveFlower(flower);
});
};
}
var A = {
receiveFlower:function(flower){
console.log("收到了" + flower);
},
listenGoodMood:function(fn){
setTimeout(function(){//假设A的心情10秒后变好
fn();
},10000);
}
}
xiaoming.sendFlower(B);
这个就是一个完整的代理模式的例子了。
- 代理模式有代理保护和虚拟代理,缓存代理等
比如代理B可以帮A过滤掉一些没有宝马或者年龄过大的人,这种请求在B处就会被处理掉,这种就叫做代理保护。
这样A可以保持良好的女神形象不直接拒绝任何人,却又不会导致太多其他骚扰。
而虚拟代理是js最常用的一种代理模式。 - 下面来看一个实际中开发的例子
虚拟代理实现图片的预加载,常见的做法是:先用一张loading图片占据位置,然后异步的方式加载图片,等加载后再填充到img标签中。
这种场合就很适合虚拟代理。我们把网速调至 5KB/s,然后通过 MyImage.setSrc 给该 img 节点设置 src,可以看到,在图片
被加载好之前,页面中有一段长长的空白时间。
var myImg = (function(){
var imgEle = document.createElement("img");
document.body.appendChild(imgEle);
return {
setSrc:function(src){
imgEle.src = src;
}
}
})();
现在开始引入代理对象 proxyImage,通过这个代理对象,在图片被真正加载好之前,页面中
将出现一张占位的菊花图 loading.gif, 来提示用户图片正在加载。代码如下:
var proxyImage = (function(){
var img = new Img();
img.onload = function(){//加载完毕后,设置为需要加载的图片地址
myImage.setSrc(this.src);
}
return {
setSrc:function(src){//在刚进入页面时,先将图片地址设置为loading
myImage.setSrc("loading.gif");
img.src =src;//然后将src赋值给新创建的img,作为一个中转缓存点
}
}
})();
proxyImage.setSrc("http://www.baidu.com/img");
//现在我们通过proxyImage间接访问myImage。proxyImage控制了客户对myImage的访问,并在此过程中添加了其他额外的操作,比如在真正的图片加载好后,先调用一张loading图。
缓存代理的例子---计算乘积
var mult = function(){
console.log("开始计算乘积");
for(var i = 0, len = arguments.length; i < len; i++){
a = a * arguments[i];
}
return a;
}
var proxyMult = (function(){
var cache = {};//缓存
return function(){
var arg = Array.prototype.join.call(arguments, ",");
if(arg in cache){
return cache[arg];
}
return cache[arg] = mult.apply(this, arguments);
}
})();
---《javascript设计模式与开发实践》
网友评论