[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); // 取緩存中的結果
```
在實際開發中,不需要去猜測是否要使用代理,當真正發現不方便直接訪問某個對象的時候,再編寫代理也不遲。