JavaScript設(shè)計模式——代理模式

代理模式

代理的概念

由于一個對象不能直接引用另一個對象,所以需要通過代理對象在這兩個對象之間起到中介的作用

以上便是代購涝桅,不,是中介耗绿,嗨苹支,代理的定義了,由此可見误阻,在我們的日常生活中债蜜,代理扮演著眾多重要的角色。

在面向?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ù)雜或者我們需要進行一定的訪問限制時,再考慮使用代理照瘾。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末吨拍,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子网杆,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件碳却,死亡現(xiàn)場離奇詭異队秩,居然都是意外死亡,警方通過查閱死者的電腦和手機昼浦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進店門馍资,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人关噪,你說我怎么就攤上這事鸟蟹。” “怎么了使兔?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵建钥,是天一觀的道長。 經(jīng)常有香客問我虐沥,道長熊经,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任欲险,我火速辦了婚禮镐依,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘天试。我一直安慰自己槐壳,他們只是感情好,可當我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布喜每。 她就那樣靜靜地躺著务唐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪灼卢。 梳的紋絲不亂的頭發(fā)上绍哎,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天,我揣著相機與錄音鞋真,去河邊找鬼崇堰。 笑死,一個胖子當著我的面吹牛涩咖,可吹牛的內(nèi)容都是我干的海诲。 我是一名探鬼主播,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼檩互,長吁一口氣:“原來是場噩夢啊……” “哼特幔!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起闸昨,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤蚯斯,失蹤者是張志新(化名)和其女友劉穎薄风,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拍嵌,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡遭赂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了横辆。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片撇他。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖狈蚤,靈堂內(nèi)的尸體忽然破棺而出困肩,到底是詐尸還是另有隱情,我是刑警寧澤脆侮,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布锌畸,位于F島的核電站,受9級特大地震影響他嚷,放射性物質(zhì)發(fā)生泄漏蹋绽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一筋蓖、第九天 我趴在偏房一處隱蔽的房頂上張望卸耘。 院中可真熱鬧,春花似錦粘咖、人聲如沸蚣抗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽翰铡。三九已至,卻和暖如春讽坏,著一層夾襖步出監(jiān)牢的瞬間锭魔,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工路呜, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留迷捧,地道東北人。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓胀葱,卻偏偏與公主長得像漠秋,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子抵屿,可洞房花燭夜當晚...
    茶點故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內(nèi)容