代理模式
代理的概念
由於一個對象不能直接引用另一個對象,所以需要通過代理對象在這兩個對象之間起到中介的作用
以上便是代購,不,是中介,嗨,代理的定義了,由此可見,在我們的日常生活中,代理扮演着衆多重要的角色。
在面向對象的編程中,代理模式的合理使用,能夠很好的體現下面兩條原則:
1、單一職責原則:面向對象設計中鼓勵將不同的職責分佈到細粒度的對象中,
Proxy
在原對象的基礎上進行了功能的衍生而又不影響原對象,符合松耦合高內聚的設計理念。
2、開發-封閉原則:代理可以隨時從程序中去除,而無需對其他部分的代碼進行修改,在實際場景中,隨着版本的迭代可能會有多種原因不在不需要代理,那麼就可以很容易的將代理對象換成原對象的調用。
代理的實現
// 你自己
var self = {
buyGoods: function(name) {
console.log('買了想要的' + name)
}
}
// 你想買的物品
var Goods = function(name) {
this.name = name
}
Goods.prototype.getName = function() {
return this.name
}
// 一個代購
var assistant = {
buyGoods: function(goods) {
self.buyGoods(goods.getName())
}
}
assistant.buyGoods(new Goods('iPhone 11 Pro Max')) //買了想要的iPhone 11 Pro Max
一個簡單的代理便躍然紙上了。
虛擬代理
把一些開銷很大的對象,延遲到真正需要它的時候纔去創建執行。
圖片的預加載便是常見的虛擬代理的應用。當圖片過大或者網絡不佳時,我們不會直接給某個img
標籤節點設置src
屬性,而是使用一張loading
圖片作爲佔位符,然後用異步的方式來加載圖片,等到圖片加載完畢,我們再把它填充到img
的節點裏。
var myImage = (function() {
var imgNode = document.createElement('img');
document.body.appendChild(imgNode);
return {
setSrc: function(src) {
imgNode.src = src;
}
}
})();
var preImage = (function() {
var img = new Image;
img.onload = function() {
myImage.setSrc = img.src;
};
return {
setSrc: function(src) {
myImage.setSrc( '../loading.gif');
img.src = src;
}
}
})();
preImage.setSrc('https://cn.bing.com/th?id=OHR.MaldivesDragonfly_ZH-CN5949519396_1920x1080.jpg&rf=LaDigue_1920x1080.jpg&pid=hp');
緩存代理
簡單的說,對第一次運行的結果進行緩存,當再次運行相同的運算時,直接從緩存裏讀取,避免重複運算。
對於複雜運算而言,可以使用緩存對象,可以提高性能。
// 計算加法
var plus = function(){
var a = 0;
for(var i = 0,ilen = arguments.length; i < ilen; i+=1) {
a += arguments[i];
}
return a;
}
// 代理函數
var proxyFunc = 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 = proxyFunc(mult);
console.log(proxyMult(1,2,3,4)); // 24
console.log(proxyMult(1,2,3,4)); // 緩存取 24
我們不用代理當然也能實現緩存就和,但是爲了達到單一職責,我們可以讓mult
實現求和,而緩存則放在代理中來實現。
ES6中的代理模式
ES6
提供了Proxy
構造函數,讓我們能夠輕鬆的使用代理模式:
let proxy = new Proxy(target, handler);
Proxy
構造函數傳入兩個參數,第一個參數target
表示所要代理的對象,第二個參數handler
也是一個對象,用來設置對所代理的對象的行爲。
ES6如何實現緩存代理
下面我們以沒有經過任何優化的計算斐波那契數列的函數來假設爲開銷很大的方法,這種遞歸調用在計算40
以上的斐波那契項時,就能明顯的感到延遲感。
const getFib = (number) => {
if (number <= 2) {
return 1;
} else {
return getFib(number - 1) + getFib(number - 2);
}
}�
// 創建一個緩存代理的工廠對象:
const getCacheProxy = (fn, cache = new Map()) => {
return new Proxy(fn, {
apply(target, context, args) {
const argsString = args.join(' ');
if (cache.has(argsString)) {
// 如果有緩存,直接返回緩存數據
console.log(`輸出${args}的緩存結果: ${cache.get(argsString)}`);
return cache.get(argsString);
}
const result = fn(...args);
cache.set(argsString, result);
return result;
}
})
}
// 調用方法
const getFibProxy = getCacheProxy(getFib);
getFibProxy(40); // 102334155
getFibProxy(40); // 輸出40的緩存結果: 102334155
總結
在vue3.0
中,便使用了Proxy
重寫了核心代碼數據雙向綁定,相對於Object.defineProperty
而言,使用Proxy
效率更高。代理模式雖然很方便,但仍應注意其使用場景,只有當對象的功能變得複雜或者我們需要進行一定的訪問限制時,再考慮使用代理。