[TOC]
# 代理模式
為一個(gè)對(duì)象提供一個(gè)代用品或占位符,以便控制對(duì)它的訪問(wèn)。當(dāng)客戶(hù)不方便直接訪問(wèn)一個(gè)對(duì)象或者不滿(mǎn)足需要的時(shí)候提供一個(gè)替身對(duì)象來(lái)控制對(duì)這個(gè)對(duì)象的訪問(wèn),客戶(hù)實(shí)際上訪問(wèn)的是替身對(duì)象。
## 保護(hù)代理
代理可以幫助主體過(guò)濾掉一些請(qǐng)求
## 虛擬代理
把一些開(kāi)銷(xiāo)很大的對(duì)象,延遲到真正需要它的時(shí)候才去創(chuàng)建。
### 虛擬代理實(shí)現(xiàn)圖片預(yù)加載
```javascript
var myImage = (function(){ // 主體函數(shù)
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(){ // 圖片加載成功后執(zhí)行
myImage.setSrc(this.src);
}
return {
setSrc: function(src){
myImage.setSrc('loading.gif'); // 先設(shè)置加載圖片
img.src = src;
}
}
})();
proxySetImage('xxxxx');
```
## 代理的意義
單一職責(zé):一個(gè)類(lèi)(通常也包括對(duì)象和函數(shù))而言,應(yīng)該僅有一個(gè)引起它變化的原因。面向?qū)ο笤O(shè)計(jì)鼓勵(lì)將行為分布到細(xì)粒度的對(duì)象之中。我們?cè)谔幚砥渲幸粋€(gè)職責(zé)時(shí),有可能因?yàn)槠鋸?qiáng)耦合性影響另外一個(gè)職責(zé)的實(shí)現(xiàn)。
## 代理和本體接口的一致性
如果某天不需要代理,可以直接選擇主體,這是需要保證提供接口的一致性,對(duì)客戶(hù)而言訪問(wèn)的都是同一個(gè)方法,只是主體不同。
- 用戶(hù)可以放心地請(qǐng)求代理,只關(guān)心是否能得到想要的結(jié)果
- 在任何使用本體的地方都可以替換成使用代理
## 虛擬代理合并HTTP請(qǐng)求
```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(){ // 設(shè)置定時(shí)器
setFile(cache.join('、'));
timer = null;
cache.length = 0; // 清空緩存
},2000)
}
})();
c.onclick = function(){
if(this.checked === true){
proxySendFile(this.id);
}
};
```
## 虛擬代理在惰性加載中的應(yīng)用
當(dāng)我們不想一開(kāi)始就運(yùn)行或加載某個(gè)文件時(shí),可以先用代理創(chuàng)造一個(gè)接口一樣的對(duì)象,雖然將用戶(hù)的操作先加入緩存,當(dāng)遇到某個(gè)真正執(zhí)行條件時(shí)再去緩存中取,然后調(diào)用主體。
## 緩存代理
緩存代理可以為一些開(kāi)銷(xiāo)很大的運(yùn)算結(jié)果提供暫時(shí)存儲(chǔ),在下次運(yùn)算時(shí),如果傳遞進(jìn)來(lái)的參數(shù)跟之前一致,則可以直接返回前面存儲(chǔ)的運(yùn)算結(jié)果。
## 用高階函數(shù)動(dòng)態(tài)創(chuàng)建代理
創(chuàng)建緩存代理工廠
```javascript
var createProxyFactory = function(fn){
var cache = {}; // 緩存
return function(){
var args = Array.prototype.join.call(arguments, ','); // 取參數(shù),并連接成字符串作為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); // 取緩存中的結(jié)果
```
在實(shí)際開(kāi)發(fā)中,不需要去猜測(cè)是否要使用代理,當(dāng)真正發(fā)現(xiàn)不方便直接訪問(wèn)某個(gè)對(duì)象的時(shí)候,再編寫(xiě)代理也不遲。
