js設計模式——代理模式proxy
什麼是代理模式
代理模式是為一個物件提供一個代用品或佔位符,以便控制對它的訪問。
(可以想象一下明星與經紀人的關係,明星是請求的本體,經紀人就是代理proxy)
如何實現代理模式
代理物件內部含有對本體物件的引用,因而可以與呼叫本體的相關方法;同時,代理物件提供與本體物件相同的介面,方便在任何時刻代理本體物件。
例子(上程式碼)
代理模式的變體有很多,有:保護代理、虛擬代理、快取代理、防火牆代理、遠端代理、智慧引用代理、寫時複製代理。具體介紹前三種。
(1)保護代理
保護代理主要用於控制不同許可權的物件對本體物件的訪問許可權。比如很多人想訪問本體A,如果有代理B存在的話,B會首先剔除不滿足A的訪問條件的訪問者,符合條件的才能訪問。
作用:過濾請求
例如:許可權的劃分和管理就是使用保護代理proxy來完成的。
註冊普通使用者:code為“001”
論壇管理者 :code為“002”
系統管理者 :code為“003”
遊 客 :code為“000”
論壇開放了四個基礎功能
1,發帖
2,帖子稽核
3,清除帖子
4,留言
遊客不具備任何操作許可權,註冊使用者只能發帖,論壇管理者可以稽核以及刪帖操作,系統管理者具有所有功能許可權。
//使用者本體 function User(name,code){ this.name = name ; this.code = code ; } ; User.prototype = { getName : function(){ return this.name ; } , getCode : function(){ return this.code ; } , post : function(){ console.log("發帖子!") ; } , remove : function(){ console.log("刪除帖子!") ; } , check : function(){ console.log("稽核帖子!") ; } , comment : function(){ console.log("回覆帖子!") ; } } ; //代理論壇類 function Forum(user){ this.user = user ; } ; Forum.prototype = { getUser : function(){ return this.user ; } , post : function(){ if(this.user.getCode() == "001" || this.user.getCode() == "003"){ return this.user.post() ; } console.log("沒許可權發帖子!") ; } , remove : function(){ if(this.user.getCode() == "002" || this.user.getCode() == "003"){ return this.user.remove() ; } console.log("沒許可權刪除帖子!") ; } , check : function(){ if(this.user.getCode() == "002" || this.user.getCode() == "003"){ return this.user.check() ; } console.log("沒許可權稽核帖子!") ; } , comment : function(){ if(this.user.getCode() == "003"){ return this.user.comment() ; } console.log("沒許可權回覆帖子!") ; } } ; //功能測試 function ForumClient(){ this.run = function(){ new Forum(new User("bigbear","003")).check() ; // 稽核帖子 } } ;
在該例子中,論壇代理有與user本體相同的介面,可以在滿足條件時,執行與本體相同的程式碼,與呼叫方法的人而言,是不透明的,我實現了呼叫,但不在乎是通過代理實現的,還是本體實現的。
(2)虛擬代理
虛擬代理是將呼叫本體方法的請求進行管理,等到本體適合執行時,再執行。
作用:將開銷很大的物件,延遲到真正需要它的時候再執行。
比如:利用虛擬代理實現圖片預載入功能:
/**在圖片預載入中實現虛擬代理 */ var myImage = (function(){ var imageNode = document.createElement('img'); document.body.appendChild(imageNode); return { setSrc: function(src){ imageNode.src = src; } } })() //代理類 var proxyImage = (function(){ var img = new Image(); img.onload = function(){ myImage.setSrc(this.src); } return { setSrc: function(src){ myImage.setSrc('本地的圖片地址'); img.src = src; //快取完畢之後會觸發img的onload事件 } } })()
比如:利用虛擬代理合並HTTP請求
/**虛擬代理合並http請求 */
//通過代理函式收集一段時間的請求,一次性發送給伺服器,減少頻繁的網路請求帶來的極大開銷
//模擬向伺服器傳送同步請求的函式
var synchronousFile = function(id){
console.log('開始同步上傳檔案,id為:'+id);
}
//代理類收集一段時間的同步請求,統一發送
var proxySynchronousFile = (function(){
var cache = [], //設定快取陣列
timer; //定時器,通過閉包訪問定時器的引用
return function(id){
cache.push(id);
if(timer){
return;
}
timer = setTimeout(function(){
synchronousFile(cache.join(','));
clearTimeout(timer);
timer = null;
cache.length = 0;
},2000)
}
})()
var checkbox = document.getElementsByTagName('input');
for(var i=0,c;c=checkbox[i++];){
c.onclick = function(){
if(this.check === true){
proxySynchronousFile(this.id);
}
}
}
在這些例子中,虛擬代理對請求進行擱置處理,等到合適的時機,對本體的介面進行呼叫,可以有效提升Web效能。
(3)快取代理
快取代理可以為開銷大的一些運算結果提供暫時性的儲存,如果再次傳進相同的引數是,直接返回結果,避免大量重複計算。
/**建立快取代理工廠 */
//將快取代理與工廠模式相結合,建立多種運算的快取代理
var mult = function(){
var a = 1;
for(var i=0;i<arguments.length;i++){
a = a*arguments[i];
}
return a;
}
var plus = function(){
var a = 0;
for(var i=0; i<arguments.length; i++){
a = a + arguments[i];
}
return a;
}
//高階函式:將函式作為引數或者返回值的函式
var proxyFactory = function(fn) {
var cache = {}; //引數快取列表
return function(){
var args = Array.prototype.join.call(arguments,',');
if(args in cache){
return cache[args];
}
//引數屬性對應的是函式
return cache[args] = fn.apply(this,arguments);
}
}
//測試
var proxyMult = proxyFactory(mult),
proxyPlus = proxyFactory(plus);
console.log(proxyMult(1,2,3,4));
console.log(proxyMult(1,2,3,4));
console.log(proxyPlus(5,6,7,8));
console.log(proxyPlus(5,6,7,8));
什麼情況下使用代理
當我們需要使用的物件很複雜或者需要很長時間去構造,這時就可以使用代理模式(Proxy)。例如:如果構建一個物件很耗費時間和計算機資源,代理模式(Proxy)允許我們控制這種情況,直到我們需要使用實際的物件。一個代理(Proxy)通常包含和將要使用的物件同樣的方法,一旦開始使用這個物件,這些方法將通過代理(Proxy)傳遞給實際的物件。
比如上面的程式碼:需要花很長的時間載入很多圖片,複雜的運算過程,頻繁的多次請求處理等;都可以用到代理模式。
小結
代理模式的一個好處就是對外部提供統一的介面方法,而代理類在介面中實現對真實類的附加操作行為,從而可以在不影響外部呼叫情況下,進行系統擴充套件。也就是說,我要修改真實角色的操作的時候,儘量不要修改他,而是在外部在“包”一層進行附加行為,即代理類。
參考書籍《JavaScript設計模式與開發實踐》
進擊的菜鳥,需要繼續努力啊!!!多學、多看、多實踐!!