代理模式
代理的概念
由于一個對象不能直接引用另一個對象,所以需要通過代理對象在這兩個對象之間起到中介的作用
以上便是代購涝桅,不,是中介耗绿,嗨苹支,代理的定義了,由此可見误阻,在我們的日常生活中债蜜,代理扮演著眾多重要的角色。
在面向?qū)ο蟮木幊讨芯糠矗砟J降暮侠硎褂醚岸ǎ軌蚝芎玫捏w現(xiàn)下面兩條原則:
1、單一職責(zé)原則:面向?qū)ο笤O(shè)計中鼓勵將不同的職責(zé)分布到細粒度的對象中精耐,
Proxy
在原對象的基礎(chǔ)上進行了功能的衍生而又不影響原對象狼速,符合松耦合高內(nèi)聚的設(shè)計理念。
2卦停、開發(fā)-封閉原則:代理可以隨時從程序中去除向胡,而無需對其他部分的代碼進行修改,在實際場景中惊完,隨著版本的迭代可能會有多種原因不在不需要代理僵芹,那么就可以很容易的將代理對象換成原對象的調(diào)用。
代理的實現(xiàn)
// 你自己
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
一個簡單的代理便躍然紙上了小槐。
虛擬代理
把一些開銷很大的對象拇派,延遲到真正需要它的時候才去創(chuàng)建執(zhí)行。
圖片的預(yù)加載便是常見的虛擬代理的應(yīng)用。當圖片過大或者網(wǎng)絡(luò)不佳時件豌,我們不會直接給某個img
標簽節(jié)點設(shè)置src
屬性疮方,而是使用一張loading
圖片作為占位符,然后用異步的方式來加載圖片茧彤,等到圖片加載完畢骡显,我們再把它填充到img
的節(jié)點里。
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');
緩存代理
簡單的說棘街,對第一次運行的結(jié)果進行緩存蟆盐,當再次運行相同的運算時,直接從緩存里讀取遭殉,避免重復(fù)運算石挂。
對于復(fù)雜運算而言,可以使用緩存對象险污,可以提高性能痹愚。
// 計算加法
var plus = function(){
var a = 0;
for(var i = 0,ilen = arguments.length; i < ilen; i+=1) {
a += arguments[i];
}
return a;
}
// 代理函數(shù)
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
我們不用代理當然也能實現(xiàn)緩存就和,但是為了達到單一職責(zé)蛔糯,我們可以讓mult
實現(xiàn)求和拯腮,而緩存則放在代理中來實現(xiàn)。
ES6中的代理模式
ES6
提供了Proxy
構(gòu)造函數(shù)蚁飒,讓我們能夠輕松的使用代理模式:
let proxy = new Proxy(target, handler);
Proxy
構(gòu)造函數(shù)傳入兩個參數(shù)动壤,第一個參數(shù)target
表示所要代理的對象,第二個參數(shù)handler
也是一個對象淮逻,用來設(shè)置對所代理的對象的行為琼懊。
ES6如何實現(xiàn)緩存代理
下面我們以沒有經(jīng)過任何優(yōu)化的計算斐波那契數(shù)列的函數(shù)來假設(shè)為開銷很大的方法,這種遞歸調(diào)用在計算40
以上的斐波那契項時爬早,就能明顯的感到延遲感哼丈。
const getFib = (number) => {
if (number <= 2) {
return 1;
} else {
return getFib(number - 1) + getFib(number - 2);
}
}?
// 創(chuàng)建一個緩存代理的工廠對象:
const getCacheProxy = (fn, cache = new Map()) => {
return new Proxy(fn, {
apply(target, context, args) {
const argsString = args.join(' ');
if (cache.has(argsString)) {
// 如果有緩存,直接返回緩存數(shù)據(jù)
console.log(`輸出${args}的緩存結(jié)果: ${cache.get(argsString)}`);
return cache.get(argsString);
}
const result = fn(...args);
cache.set(argsString, result);
return result;
}
})
}
// 調(diào)用方法
const getFibProxy = getCacheProxy(getFib);
getFibProxy(40); // 102334155
getFibProxy(40); // 輸出40的緩存結(jié)果: 102334155
總結(jié)
在vue3.0
中,便使用了Proxy
重寫了核心代碼數(shù)據(jù)雙向綁定筛严,相對于Object.defineProperty
而言醉旦,使用Proxy
效率更高。代理模式雖然很方便桨啃,但仍應(yīng)注意其使用場景车胡,只有當對象的功能變得復(fù)雜或者我們需要進行一定的訪問限制時,再考慮使用代理照瘾。