JavaScript設(shè)計(jì)模式之單例模式實(shí)例
《Practical Common Lisp》的作者 Peter Seibel 曾說(shuō),如果你需要一種模式,那一定是哪里出了問(wèn)題。他所說(shuō)的問(wèn)題是指因?yàn)檎Z(yǔ)言的天生缺陷,不得不去尋求和總結(jié)一種通用的解決方案。
不管是弱類(lèi)型或強(qiáng)類(lèi)型,靜態(tài)或動(dòng)態(tài)語(yǔ)言,命令式或說(shuō)明式語(yǔ)言、每種語(yǔ)言都有天生的優(yōu)缺點(diǎn)。一個(gè)牙買(mǎi)加運(yùn)動(dòng)員, 在短跑甚至拳擊方面有一些優(yōu)勢(shì),在練瑜伽上就欠缺一些。
術(shù)士和暗影牧師很容易成為一個(gè)出色的輔助,而一個(gè)背著梅肯滿(mǎn)地圖飛的敵法就會(huì)略顯尷尬。 換到程序中, 靜態(tài)語(yǔ)言里可能需要花很多功夫來(lái)實(shí)現(xiàn)裝飾者,而js由于能隨時(shí)往對(duì)象上面扔方法,以至于裝飾者模式在js里成了雞肋。
講 Javascript 設(shè)計(jì)模式的書(shū)還比較少,《Pro javaScript Design Patterns》是比較經(jīng)典的一本,但是它里面的例子舉得比較啰嗦,所以結(jié)合我在工作中寫(xiě)過(guò)的代碼,把我的理解總結(jié)一下。如果我的理解出現(xiàn)了偏差,請(qǐng)不吝指正。
一、單例模式
單例模式的定義是產(chǎn)生一個(gè)類(lèi)的唯一實(shí)例,但js本身是一種“無(wú)類(lèi)”語(yǔ)言。很多講js設(shè)計(jì)模式的文章把{}當(dāng)成一個(gè)單例來(lái)使用也勉強(qiáng)說(shuō)得通。因?yàn)閖s生成對(duì)象的方式有很多種,我們來(lái)看下另一種更有意義的單例。
有這樣一個(gè)常見(jiàn)的需求,點(diǎn)擊某個(gè)按鈕的時(shí)候需要在頁(yè)面彈出一個(gè)遮罩層。比如web.qq.com點(diǎn)擊登錄的時(shí)候.
這個(gè)生成灰色背景遮罩層的代碼是很好寫(xiě)的.
var createMask = function(){
return document,body.appendChild( document.createElement(div) );
}
$( ‘button' ).click( function(){
Var mask = createMask();
mask.show();
})
問(wèn)題是, 這個(gè)遮罩層是全局唯一的, 那么每次調(diào)用createMask都會(huì)創(chuàng)建一個(gè)新的div, 雖然可以在隱藏遮罩層的把它remove掉. 但顯然這樣做不合理.
再看下第二種方案, 在頁(yè)面的一開(kāi)始就創(chuàng)建好這個(gè)div. 然后用一個(gè)變量引用它.
var mask = document.body.appendChild( document.createElement( ”div' ) );
$( ”button' ).click( function(){
mask.show();
} )
這樣確實(shí)在頁(yè)面只會(huì)創(chuàng)建一個(gè)遮罩層div, 但是另外一個(gè)問(wèn)題隨之而來(lái), 也許我們永遠(yuǎn)都不需要這個(gè)遮罩層, 那又浪費(fèi)掉一個(gè)div, 對(duì)dom節(jié)點(diǎn)的任何操作都應(yīng)該非常吝嗇.
如果可以借助一個(gè)變量. 來(lái)判斷是否已經(jīng)創(chuàng)建過(guò)div呢?
var mask;
var createMask = function(){
if ( mask ) return mask;
else{
mask = document,body.appendChild( document.createElement(div) );
return mask;
}
}
看起來(lái)不錯(cuò), 到這里的確完成了一個(gè)產(chǎn)生單列對(duì)象的函數(shù). 我們?cè)僮屑?xì)看這段代碼有什么不妥.
首先這個(gè)函數(shù)是存在一定副作用的, 函數(shù)體內(nèi)改變了外界變量mask的引用, 在多人協(xié)作的項(xiàng)目中, createMask是個(gè)不安全的函數(shù). 另一方面, mask這個(gè)全局變量并不是非需不可. 再來(lái)改進(jìn)一下.
var createMask = function(){
var mask;
return function(){
return mask || ( mask = document.body.appendChild( document.createElement(‘div') ) )
}
}()
用了個(gè)簡(jiǎn)單的閉包把變量mask包起來(lái), 至少對(duì)于createMask函數(shù)來(lái)講, 它是封閉的.
可能看到這里, 會(huì)覺(jué)得單例模式也太簡(jiǎn)單了. 的確一些設(shè)計(jì)模式都是非常簡(jiǎn)單的, 即使從沒(méi)關(guān)注過(guò)設(shè)計(jì)模式的概念, 在平時(shí)的代碼中也不知不覺(jué)用到了一些設(shè)計(jì)模式. 就像多年前我明白老漢推車(chē)是什么回事的時(shí)候也想過(guò)尼瑪原來(lái)這就是老漢推車(chē).
GOF里的23種設(shè)計(jì)模式, 也是在軟件開(kāi)發(fā)中早就存在并反復(fù)使用的模式. 如果程序員沒(méi)有明確意識(shí)到他使用過(guò)某些模式, 那么下次他也許會(huì)錯(cuò)過(guò)更合適的設(shè)計(jì) (這段話(huà)來(lái)自《松本行弘的程序世界》).
再回來(lái)正題, 前面那個(gè)單例還是有缺點(diǎn). 它只能用于創(chuàng)建遮罩層. 假如我又需要寫(xiě)一個(gè)函數(shù), 用來(lái)創(chuàng)建一個(gè)唯一的xhr對(duì)象呢? 能不能找到一個(gè)通用的singleton包裝器.
js中函數(shù)是第一型, 意味著函數(shù)也可以當(dāng)參數(shù)傳遞. 看看最終的代碼.
var singleton = function( fn ){
var result;
return function(){
return result || ( result = fn .apply( this, arguments ) );
}
}
var createMask = singleton( function(){
return document.body.appendChild( document.createElement(‘div') );
})
用一個(gè)變量來(lái)保存第一次的返回值, 如果它已經(jīng)被賦值過(guò), 那么在以后的調(diào)用中優(yōu)先返回該變量. 而真正創(chuàng)建遮罩層的代碼是通過(guò)回調(diào)函數(shù)的方式傳人到singleton包裝器中的. 這種方式其實(shí)叫橋接模式. 關(guān)于橋接模式, 放在后面一點(diǎn)點(diǎn)來(lái)說(shuō).
然而singleton函數(shù)也不是完美的, 它始終還是需要一個(gè)變量result來(lái)寄存div的引用. 遺憾的是js的函數(shù)式特性還不足以完全的消除聲明和語(yǔ)句.
- JavaScript設(shè)計(jì)模式---單例模式詳解【四種基本形式】
- JS 設(shè)計(jì)模式之:?jiǎn)卫J蕉x與實(shí)現(xiàn)方法淺析
- javascript設(shè)計(jì)模式 – 單例模式原理與應(yīng)用實(shí)例分析
- 《javascript設(shè)計(jì)模式》學(xué)習(xí)筆記三:Javascript面向?qū)ο蟪绦蛟O(shè)計(jì)單例模式原理與實(shí)現(xiàn)方法分析
- js設(shè)計(jì)模式之單例模式原理與用法詳解
- JavaScript設(shè)計(jì)模式之單例模式原理與用法實(shí)例分析
- JavaScript設(shè)計(jì)模式之單例模式簡(jiǎn)單實(shí)例教程
- JS基于設(shè)計(jì)模式中的單例模式(Singleton)實(shí)現(xiàn)封裝對(duì)數(shù)據(jù)增刪改查功能
- JS設(shè)計(jì)模式之單例模式(一)
- NodeJS設(shè)計(jì)模式總結(jié)【單例模式,適配器模式,裝飾模式,觀察者模式】
- 學(xué)習(xí)JavaScript設(shè)計(jì)模式之單例模式
- JavaScript中的設(shè)計(jì)模式 單例模式
相關(guān)文章
基于JS實(shí)現(xiàn)父組件的請(qǐng)求服務(wù)過(guò)程解析
這篇文章主要介紹了基于JS實(shí)現(xiàn)父組件的請(qǐng)求服務(wù)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10JS函數(shù)報(bào)錯(cuò)Uncaught ReferenceError: XX is not
這篇文章主要介紹了JS函數(shù)報(bào)錯(cuò)Uncaught ReferenceError: XX is not defined問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10JS實(shí)現(xiàn)購(gòu)物車(chē)特效
本文主要分享了用JavaScript實(shí)現(xiàn)購(gòu)物車(chē)特效的示例代碼。具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧2017-02-02JavaScript Canvas繪制圓形時(shí)鐘效果
這篇文章主要為大家詳細(xì)介紹了JavaScript Canvas繪制圓形時(shí)鐘效果的相關(guān)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04復(fù)制小說(shuō)文本時(shí)出現(xiàn)的隨機(jī)亂碼的去除方法
想把小說(shuō)復(fù)制下來(lái)慢慢看,卻發(fā)現(xiàn)復(fù)制到記事本里出現(xiàn)一大堆亂七八糟的東西,很是不爽。于是就想了個(gè)簡(jiǎn)單的辦法把它干掉了。2010-09-09JS實(shí)現(xiàn)百度網(wǎng)盤(pán)任意文件強(qiáng)制下載功能
這篇文章主要介紹了JS實(shí)現(xiàn)百度網(wǎng)盤(pán)任意文件強(qiáng)制下載 ,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-08-08直接拿來(lái)用的頁(yè)面跳轉(zhuǎn)進(jìn)度條JS實(shí)現(xiàn)
這篇文章主要為大家分享了一款直接拿來(lái)用的頁(yè)面跳轉(zhuǎn)進(jìn)度條,由javascript實(shí)現(xiàn),可以直接跳轉(zhuǎn)到相應(yīng)頁(yè)面,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-01-01JavaScript實(shí)現(xiàn)網(wǎng)頁(yè)視頻添加水印的示例代碼
這篇文章主要介紹了通過(guò)js給網(wǎng)頁(yè)視頻添加水印,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-02-02JS解決回調(diào)地獄為什么需要Promise來(lái)優(yōu)化異步編程
這篇文章主要為大家介紹了JS解決回調(diào)地獄為什么需要Promise來(lái)優(yōu)化異步編程原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10微信小程序scroll-view隱藏滾動(dòng)條的方法詳解
這篇文章主要介紹了微信小程序scroll-view隱藏滾動(dòng)條的方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-03-03