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

代理模式是為一個對象提供一個代用品或占位符括荡,以便控制對它的訪問。

????代理模式是一種非常有意義的模式提揍,在生活中可以找到很多代理模式的場景低匙。比如,明星都有經(jīng)紀人作為代理碳锈。如果想請明星來辦一場商業(yè)演出顽冶,只能聯(lián)系他的經(jīng)紀人。經(jīng)紀人會把商業(yè)演出的細節(jié)和報酬都談好之后售碳,再把合同交給明星簽强重。

????代理模式的關(guān)鍵是,當(dāng)客戶不方便直接訪問一個對象或者不滿足需要的時候贸人,提供一個替身對象來控制對這個對象的訪問间景,客戶實際上訪問的是替身對象。替身對象對請求做出一些處理之后艺智,再把請求轉(zhuǎn)交給本體對象倘要。如圖 所示。


不用代理模式


使用代理模式

保護代理和虛擬代理

????保護代理用于控制不同權(quán)限的對象對目標對象的訪問十拣,但在 JavaScript 并不容易實現(xiàn)保護代理封拧,因為我們無法判斷誰訪問了某個對象。而虛擬代理是最常用的一種代理模式夭问,我們主要討論的也是虛擬代理泽西。

虛擬代理實現(xiàn)圖片預(yù)加載

????在 Web 開發(fā)中,圖片預(yù)加載是一種常用的技術(shù)缰趋,如果直接給某個 img 標簽節(jié)點設(shè)置 src 屬性捧杉,由于圖片過大或者網(wǎng)絡(luò)不佳陕见,圖片的位置往往有段時間會是一片空白。常見的做法是先用一張loading 圖片占位味抖,然后用異步的方式加載圖片评甜,等圖片加載好了再把它填充到 img 節(jié)點里,這種場景就很適合使用虛擬代理仔涩。

????下面我們來實現(xiàn)這個虛擬代理忍坷,首先創(chuàng)建一個普通的本體對象,這個對象負責(zé)往頁面中創(chuàng)建一個 img 標簽红柱,并且提供一個對外的 setSrc 接口承匣,外界調(diào)用這個接口,便可以給該 img 標簽設(shè)置src 屬性:

var myImage = (function(){

????var imgNode = document.createElement( 'img' );

????document.body.appendChild( imgNode );

????return {

????????setSrc: function( src ){

????????????imgNode.src = src;

????????}

????}

})();

myImage.setSrc( 'http:// imgcache.qq.com/music/photo/k/000GGDys0yA0Nk.jpg' );

????我們把網(wǎng)速調(diào)至 5KB/s锤悄,然后通過 MyImage.setSrc 給該 img 節(jié)點設(shè)置 src韧骗,可以看到,在圖片被加載好之前零聚,頁面中有一段長長的空白時間袍暴。

????現(xiàn)在開始引入代理對象 proxyImage,通過這個代理對象隶症,在圖片被真正加載好之前政模,頁面中將出現(xiàn)一張占位的菊花圖 loading.gif, 來提示用戶圖片正在加載。代碼如下:

var myImage = (function(){

????var imgNode = document.createElement( 'img' );

????document.body.appendChild( imgNode );

????return {

????????setSrc: function( src ){

????????????imgNode.src = src;

? ? ? ? }

? ? }

})();

var proxyImage = (function(){

????var img = new Image;

????img.onload = function(){? ? // 圖片加載完成后觸發(fā)

????????myImage.setSrc( this.src ); // this => img? ?即 img.src

? ? }

????return {

????????setSrc: function( src ){

????????????myImage.setSrc( 'file:// /C:/Users/svenzeng/Desktop/loading.gif' );

????????????img.src = src;

????????}

????}

})();

proxyImage.setSrc( 'http:// imgcache.qq.com/music/photo/k/000GGDys0yA0Nk.jpg' );

????現(xiàn)在我們通過 proxyImage 間接地訪問 MyImage蚂会。proxyImage 控制了客戶對 MyImage 的訪問淋样,并且在此過程中加入一些額外的操作,比如在真正的圖片加載好之前胁住,先把 img 節(jié)點的 src 設(shè)置為一張本地的 loading 圖片趁猴。

? ??縱觀整個程序,我們并沒有改變或者增加 MyImage 的接口彪见,但是通過代理對象儡司,實際上給系統(tǒng)添加了新的行為。這是符合開放—封閉原則的余指。給 img 節(jié)點設(shè)置 src 和圖片預(yù)加載這兩個功能捕犬,被隔離在兩個對象里,它們可以各自變化而不影響對方酵镜。何況就算有一天我們不再需要預(yù)加載碉碉,那么只需要改成請求本體而不是請求代理對象即可。

緩存代理

? ? 緩存代理可以為一些開銷大的運算結(jié)果提供暫時的存儲笋婿,在下次運算時誉裆,如果傳遞進來的參數(shù)跟之前一致,則可以直接返回前面存儲的運算結(jié)果缸濒。

緩存代理的例子——計算乘積

先創(chuàng)建一個用于求乘積的函數(shù):

var mult = function(){

????console.log( '開始計算乘積' );

????var a = 1;

????for ( var i = 0, l = arguments.length; i < l; i++ ){

????????a = a * arguments[i];

????}

????return a;

};

mult( 2, 3 ); // 輸出:6

mult( 2, 3, 4 ); // 輸出:24

// 現(xiàn)在加入緩存代理函數(shù):

var proxyMult = (function(){

????var cache = {};

????return function(){

????????var args = Array.prototype.join.call( arguments, ',' );

????????if ( args in cache ){

????????????return cache[ args ];

????????}

????????return cache[ args ] = mult.apply( this, arguments );

? ? ?}

})();

proxyMult( 1, 2, 3, 4 ); // 輸出:24

proxyMult( 1, 2, 3, 4 ); // 輸出:24

????當(dāng)我們第二次調(diào)用 proxyMult( 1, 2, 3, 4 )的時候足丢,本體 mult 函數(shù)并沒有被計算,proxyMult直接返回了之前緩存好的計算結(jié)果庇配。

????通過增加緩存代理的方式斩跌,mult 函數(shù)可以繼續(xù)專注于自身的職責(zé)——計算乘積,緩存的功能是由代理對象實現(xiàn)的捞慌。

用高階函數(shù)動態(tài)創(chuàng)建代理

????通過傳入高階函數(shù)這種更加靈活的方式耀鸦,可以為各種計算方法創(chuàng)建緩存代理。現(xiàn)在這些計算方法被當(dāng)作參數(shù)傳入一個專門用于創(chuàng)建緩存代理的工廠中啸澡, 這樣一來袖订,我們就可以為乘法、加

法嗅虏、減法等創(chuàng)建緩存代理洛姑,代碼如下:

/**************** 計算乘積 *****************/

var mult = function(){

????var a = 1;

????for ( var i = 0, l = arguments.length; i < l; i++ ){

????????a = a * arguments[i];

????}

????return a;

};

/**************** 計算加和 *****************/

var plus = function(){

????var a = 0;

????for ( var i = 0, l = arguments.length; i < l; i++ ){

????????a = a + arguments[i];

????}

????return a;

};

/**************** 創(chuàng)建緩存代理的工廠 *****************/

var createProxyFactory = 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 = createProxyFactory( mult ),

proxyPlus = createProxyFactory( plus );

alert ( proxyMult( 1, 2, 3, 4 ) ); // 輸出:24

alert ( proxyMult( 1, 2, 3, 4 ) ); // 輸出:24

alert ( proxyPlus( 1, 2, 3, 4 ) ); // 輸出:10

alert ( proxyPlus( 1, 2, 3, 4 ) ); // 輸出:10

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ——摘自《JavaScript設(shè)計模式與開發(fā)實踐 》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市皮服,隨后出現(xiàn)的幾起案子楞艾,更是在濱河造成了極大的恐慌,老刑警劉巖龄广,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件硫眯,死亡現(xiàn)場離奇詭異,居然都是意外死亡择同,警方通過查閱死者的電腦和手機两入,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來敲才,“玉大人裹纳,你說我怎么就攤上這事」榻铮” “怎么了痊夭?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長脏里。 經(jīng)常有香客問我她我,道長,這世上最難降的妖魔是什么迫横? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任番舆,我火速辦了婚禮,結(jié)果婚禮上矾踱,老公的妹妹穿的比我還像新娘恨狈。我一直安慰自己,他們只是感情好呛讲,可當(dāng)我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布禾怠。 她就那樣靜靜地躺著返奉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪吗氏。 梳的紋絲不亂的頭發(fā)上芽偏,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天,我揣著相機與錄音弦讽,去河邊找鬼污尉。 笑死,一個胖子當(dāng)著我的面吹牛往产,可吹牛的內(nèi)容都是我干的被碗。 我是一名探鬼主播,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼仿村,長吁一口氣:“原來是場噩夢啊……” “哼锐朴!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起奠宜,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤包颁,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后压真,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體娩嚼,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年滴肿,在試婚紗的時候發(fā)現(xiàn)自己被綠了岳悟。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡泼差,死狀恐怖贵少,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情堆缘,我是刑警寧澤滔灶,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站吼肥,受9級特大地震影響录平,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜缀皱,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一斗这、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧啤斗,春花似錦表箭、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽彼水。三九已至,卻和暖如春伯襟,著一層夾襖步出監(jiān)牢的瞬間猿涨,已是汗流浹背握童。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工姆怪, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人澡绩。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓稽揭,卻偏偏與公主長得像,于是被迫代替她去往敵國和親肥卡。 傳聞我的和親對象是個殘疾皇子溪掀,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,828評論 2 345