[TOC]
# 代理模式
为一个对象提供一个代用品或占位符,以便控制对它的访问。当客户不方便直接访问一个对象或者不满足需要的时候提供一个替身对象来控制对这个对象的访问,客户实际上访问的是替身对象。
## 保护代理
代理可以帮助主体过滤掉一些请求
## 虚拟代理
把一些开销很大的对象,延迟到真正需要它的时候才去创建。
### 虚拟代理实现图片预加载
```javascript
var myImage = (function(){ // 主体函数
var imageNode = document.createElement('img');
document.body.appendChild(imageNode);
return {
setSrc: function(src){
imageNoode.src = src;
}
}
})();
var proxySetImage = (function(){ // 代理
var img = new Image();
img.onload = function(){ // 图片加载成功后执行
myImage.setSrc(this.src);
}
return {
setSrc: function(src){
myImage.setSrc('loading.gif'); // 先设置加载图片
img.src = src;
}
}
})();
proxySetImage('xxxxx');
```
## 代理的意义
单一职责:一个类(通常也包括对象和函数)而言,应该仅有一个引起它变化的原因。面向对象设计鼓励将行为分布到细粒度的对象之中。我们在处理其中一个职责时,有可能因为其强耦合性影响另外一个职责的实现。
## 代理和本体接口的一致性
如果某天不需要代理,可以直接选择主体,这是需要保证提供接口的一致性,对客户而言访问的都是同一个方法,只是主体不同。
- 用户可以放心地请求代理,只关心是否能得到想要的结果
- 在任何使用本体的地方都可以替换成使用代理
## 虚拟代理合并HTTP请求
```javascript
var setFile = function(file){
console.log('send file:' + file);
};
var proxySendFile = (function(){
var cache = [], timer;
return function(file){
cache.push(file); // 先加入缓存中
if(timer){
return;
}
timer = setTimeout(function(){ // 设置定时器
setFile(cache.join('、'));
timer = null;
cache.length = 0; // 清空缓存
},2000)
}
})();
c.onclick = function(){
if(this.checked === true){
proxySendFile(this.id);
}
};
```
## 虚拟代理在惰性加载中的应用
当我们不想一开始就运行或加载某个文件时,可以先用代理创造一个接口一样的对象,虽然将用户的操作先加入缓存,当遇到某个真正执行条件时再去缓存中取,然后调用主体。
## 缓存代理
缓存代理可以为一些开销很大的运算结果提供暂时存储,在下次运算时,如果传递进来的参数跟之前一致,则可以直接返回前面存储的运算结果。
## 用高阶函数动态创建代理
创建缓存代理工厂
```javascript
var createProxyFactory = function(fn){
var cache = {}; // 缓存
return function(){
var args = Array.prototype.join.call(arguments, ','); // 取参数,并连接成字符串作为key
if(args in cache){ // 判断是否存在于缓存中
return cache[args];
}
return cache[args] = fn.apply(this, arguments); // 加入缓存
}
}
var proxyA = createProxyFactory(A);
proxyA(1,2,3);
proxyA(1,2,3); // 取缓存中的结果
```
在实际开发中,不需要去猜测是否要使用代理,当真正发现不方便直接访问某个对象的时候,再编写代理也不迟。