一、定義 當客戶不方便直接訪問一個對象或者不滿足需要的時候,提供一個對象來控制堆這個對象的訪問。 二、舉例 惰性單例模式的實現依靠緩存代理 三、結構 代理模式需要一個本體對象和一個代理對象。在代理模式下,對於本體對象的特定的操作通過代理對象進行。如圖所示 這種模式的關鍵點在於:本體對象和代理對象介面 ...
一、定義
當客戶不方便直接訪問一個對象或者不滿足需要的時候,提供一個對象來控制堆這個對象的訪問。
二、舉例
惰性單例模式的實現依靠緩存代理
三、結構
代理模式需要一個本體對象和一個代理對象。在代理模式下,對於本體對象的特定的操作通過代理對象進行。如圖所示
這種模式的關鍵點在於:本體對象和代理對象介面的一致性。也就是說如果需要不通過代理進行操作,那麼直接操作本體對象依然可以。
四、實現
代理模式分為很多類,其中經常用到的有保護代理、虛擬代理、緩存代理。
1.保護代理
保護代理的作用是當對本體對象的屬性進行訪問和賦值時,代理對象可以對其進行攔截。
在ES6中,代理是一個已經被實現的知識點,如下:
var obj = { _name: "JYY", age: 29 }; var objProxy = new Proxy(obj, { get: function(target, key){ if(key === "_name") return undefined; return target[key]; }, set: function(target, key){ if(key === "_name") return; } }); objProxy._name; // undefined objProxy.age; // 29
上面的代碼中obj是本體對象,objProxy是代理對象,這個代理實現了禁止訪問和設置內部私有屬性的功能。在這段代碼中,我們使用代理對象對本體對象的get和set方法進行了代理,本體對象和代理對象都包含了這個set和get介面(obj對象本身內部實現了get和set),所以如果我們繞過代理對象直接訪問和賦值本體對象也是可以的:
var obj = { _name: "JYY", age: 29 }; console.log(obj._name); //JYY obj._name = "hah"; console.log(obj._name); // hah
保護代理的重點在於,代理對象保護外界對於本體對象的可訪問和可操作性,也就是說在保護代理中,代理對象是用於禁止外界對本體對象的操作,防止本體對象的屬性被外界進行操作
2.虛擬代理
虛擬代理在我理解,就是用戶認為已經執行了某個功能,事實上卻時使用代理對象進行占位,待觸發的時機到來,才會真正的執行本體對象的操作。也就是虛擬代理把一些開銷很大的對象,延遲到真正需要他的時候才採取創建。因此虛擬代理的使用伴隨的是性能的提升。
典型的虛擬代理的例子就是節流,如下所示:
var resizeProxy = function(fn){ let timer = null; return function(){ if(!timer){ timer = setTimeout(function(){ fn && fn(); timer = null; },1000) } } }; var resizeChange = function(){ console.log(1); }; window.onresize = resizeProxy(resizeChange);
這段代碼是經典的使用節流控制窗體尺寸大小改變事件觸發的例子,作用就是讓resize事件的觸發不那麼頻繁,自定義的控制觸發的頻率,這對於性能的提升很有幫助。
在這段代碼裡面,resizeChange就是本體,resizeProxy就是代理,如果不使用代理直接將fn複製給window.onresize依舊是可用的。而使用代理的作用就是首先進行占位,開發中代理內部的代碼是不可見的(比如提供的是api),利用resizeProxy的不可見性,讓客戶認為已經調用了功能代碼,但是事實上是我們並未立即執行代碼,因為我們作為開發者知道立即執行會帶來性能的喪失,只有在合適的時機我們才會委托代理執行本地代碼,執行真正的業務代碼。
3.緩存代理
緩存代理在單例模式中就已出現,用於創建惰性代理模式,其原理就是在需要時創建對象,並將該對象保存在閉包中,這樣可以一次創建多次使用。
當然在惰性單例中使用是緩存代理最簡單的實現方式。在實際開發中,我們可能會遇到這樣的場景,在tab中每個tab頁都包含多個圖表,因此在點擊tab的時候就需要獲取這個tab對應的圖表的數據,這些數據都需要從後端請求。比如以某個省的地市經濟統計情況為例,我們需要的頁面如下:
可見這樣的情況下,用戶很可能不斷地點擊tab切換城市,然後和其他城市進行對比。如果每次點擊都請求數據,帶給用戶的體驗會非常差。這樣的情況下,我們就可以做個全局的緩存,當然你可以保存在組件的狀態中,除此之外緩存代理也是個很棒的選擇。代碼如下:
// 請求數據 function requestData(regionName){ return new Promise(function(resolve, reject){ var rest = ajax({ type: "post", params: {regionName: regionName} }) resolve(rest); }) } // 初始化數據代理方法 var proxyInit = (function(){ var cache = {}; return async function(regionName){ if(cache[regionName]){ return cache[regionName]; }else{ return cache[regionName] = await requestData(regionName); } } }); proxyInit("石家莊");
五、總結
三種類型雖然均為代理模式,但是各自的目的並不相同,保護代理是為了阻止外部對內部對象的訪問或者是操作等;虛擬代理是為了提升性能,延遲本體執行,在合適的時機進行觸發,目的是減少本體的執行次數;緩存代理同樣是為了提升性能,但是為了減緩記憶體的壓力,同樣的屬性,在記憶體中只保留一份。