JavaScript中實(shí)現(xiàn)單體模式分享
單體模式作為一種軟件開發(fā)模式在眾多面向?qū)ο笳Z言中得到了廣泛的使用,在javascript中,單體模式也是使用非常廣泛的,但是由于javascript語言擁有其獨(dú)特的面向?qū)ο蠓绞?,?dǎo)致其和一些傳統(tǒng)面向?qū)ο笳Z言雖然在單體模式的思想上是一致的,但是實(shí)現(xiàn)起來還是有差異的。
首先來看看傳統(tǒng)面向?qū)ο笳Z言對于單體模式的定義:單體模式是只能被實(shí)例化一次并且可以通過一個(gè)眾所周知的訪問點(diǎn)來訪問的類。這個(gè)定義有兩點(diǎn)突出了傳統(tǒng)面向?qū)ο笳Z言的特征,即類和實(shí)例化,所以對于傳統(tǒng)面向?qū)ο笳Z言來講,單體模式是建立在其類和實(shí)例化的自然特性之上的,即使用關(guān)鍵字class定義一個(gè)類,該類可通過new關(guān)鍵字來實(shí)例化,但是需要保證每次被new實(shí)例化之后得到的都是同一個(gè)實(shí)例或者說只能通過new來調(diào)用其構(gòu)造函數(shù)一次。
再來看看javascript中對于單體模式的定義:單體是一個(gè)用來劃分命名空間并將一批相關(guān)方法和屬性組織在一起的對象,如果它能夠被實(shí)例化,那么只能被實(shí)例化一次。對比上面的定義,你會發(fā)現(xiàn)這里的單體定義將其實(shí)質(zhì)定義為對象,而不是傳統(tǒng)面向?qū)ο笳Z言中的類,這也表明了javascript這門語言是基于對象的。同時(shí)后面又指出,如果能夠被實(shí)例化,這說明了在javascript中單體定義應(yīng)該有好幾種方式,存在一種或幾種能夠被實(shí)例化即使用new關(guān)鍵字來創(chuàng)建單體對象的方式,但是這種方式不是javascript自身的自然特征,因?yàn)槭褂胣ew關(guān)鍵字創(chuàng)造出來的對象,實(shí)際上都是通過function來模擬定義其構(gòu)造函數(shù)的(雖然ES6開始支持class關(guān)鍵字了,但是目前還沒有得到瀏覽器廣泛支持),那么如何使用javascript的自然特征來實(shí)現(xiàn)單體模式呢?
var Singleton={ attribute1:true, attribute2:10, method1:function(){ }, method2:function(arg){ } }
這里定義了一個(gè)對象Singleton,內(nèi)部包含若干屬性和方法,將其包含在頁面中,js載入的時(shí)候就創(chuàng)建了這個(gè)對象,在調(diào)用時(shí)使用Singleton.method1來調(diào)用,它的實(shí)例化是隨著頁面載入js解析執(zhí)行過程中完成的,我們并沒有使用new關(guān)鍵字來實(shí)例化這個(gè)對象,這也是javascript中實(shí)現(xiàn)單體模式和傳統(tǒng)面向?qū)ο笳Z言一個(gè)很大的不同。這種方式更為簡單易于理解。但是這種方式存在若干缺點(diǎn),一個(gè)很明顯的缺點(diǎn)是它并沒有提供命名空間,其他程序員如果在頁面中也定義了一個(gè)Singleton變量,那么很容易改寫和混淆這個(gè)單體對象,于是針對這個(gè)問題,將其改寫如下:
var mySpace={}; mySpace.Singleton={ attribute1:true, attribute2:10, method1:function(){ }, method2:function(arg){ } }
這里首先定義了一個(gè)mySpace的命名空間,然后將單體對象Singleton掛載在這個(gè)對象的下面,這大大減少了和其他程序員沖突以及誤操作的可能,即使其他人在全局作用域中定義一個(gè)Singleton變量,也不會污染到這個(gè)單體對象,這就實(shí)現(xiàn)了前面定義中所說的劃分命名空間并且將一些相關(guān)屬性和方法組織在一起的功能。
這個(gè)方法依然存在缺點(diǎn),這個(gè)單體對象的所有屬性和方法都是共有的,外部可隨時(shí)訪問和修改,于是采用閉包來模擬私有屬性和方法,如下:
mySpace.Singleton=(function(){ var privateAttribute1=false; var privateAttribute1=[1,2,3]; function privateMethod1(){ } function privateMethod2(){ } return { publicAttribute1:true, publicAttribute2:10, publicMethod1:function(){ privateAttribute1=true; privateMethod1(); }, publicMethod2:function(arg){ privateAttribute1=[4,5,6]; privateMethod2(); } } })();
在這里我們直接給該單體對象賦值了一個(gè)匿名自執(zhí)行的函數(shù),在該函數(shù)中使用var和function關(guān)鍵字分別來定義其私有屬性和方法,這些在函數(shù)外部(單體對象外部)是無法直接訪問的,因?yàn)楹瘮?shù)一執(zhí)行完畢,其內(nèi)部作用域的空間就會被回收,這也就是能夠利用閉包來模擬私有屬性和方法的原因所在。在該函數(shù)(閉包)中,同時(shí)最終返回一個(gè)對象,這個(gè)對象中包含一些公有方法和屬性,在外部可以直接調(diào)用,同時(shí)這些公有方法由于定義在函數(shù)內(nèi)部,所以可以調(diào)用其私有屬性和方法,但是外界只能通過返回的公有方法和屬性來完成某些操作,不能夠直接調(diào)用Singleton.privateMethod1這些屬性。這就使得該單體對象既隔離了外界去直接訪問其私有屬性和方法,又提供給外界一些共有屬性和方法去完成某些操作。
這種匿名函數(shù)自執(zhí)行所構(gòu)造的單體模式在很多js庫中被廣泛使用,但是依然存在一個(gè)問題,如果我們在載入頁面的時(shí)候并不需要用到該對象,而且該對象的創(chuàng)建比較耗費(fèi)開銷(如需要進(jìn)行大量計(jì)算或需要多次訪問dom樹及其屬性等)時(shí),合理的做法是需要它的時(shí)候再去創(chuàng)建它,而不是隨著js的解析執(zhí)行直接去創(chuàng)建,這種概念被稱之為惰性加載(lazy loading),于是修改以上代碼如下:
mySpace.Singleton=(function(){ var uniqueInstance; function constructor(){ var privateAttribute1=false; var privateAttribute1=[1,2,3]; function privateMethod1(){ } function privateMethod2(){ } return { publicAttribute1:true, publicAttribute2:10, publicMethod1:function(){ privateAttribute1=true; privateMethod1(); }, publicMethod2:function(arg){ privateAttribute1=[4,5,6]; privateMethod2(); } } } return { getInstance:function(){ if(!uniqueInstance){ uniqueInstance=constructor(); } return uniqueInstance; } } })();
這里首先在匿名函數(shù)中定義了一個(gè)私有變量uniqueInstance,作為一個(gè)判斷單體對象是否被創(chuàng)建出來的句柄,然后將剛才所有對單體對象定義的屬性和方法都放在一個(gè)名為constructor的函數(shù)中,只有該函數(shù)調(diào)用了,才會創(chuàng)造出該單體對象,否則不會直接創(chuàng)建它。然后,返回一個(gè)對象,其包含一個(gè)getInstance方法,該方法是供外部調(diào)用的,調(diào)用該方法的時(shí)候首先判斷該單體對象是否存在,如果存在就直接返回它,否則調(diào)用constructor函數(shù)構(gòu)造這個(gè)單體對象再返回它。最后如果我們調(diào)用該單體對象的某個(gè)方法,需要使用mySpace.Singleton.getInstance().publicMethod1(),這里,只有我們這樣調(diào)用的時(shí)候才會創(chuàng)建這個(gè)單體對象,否則該單體對象是不會被自動創(chuàng)建的,這實(shí)際上就實(shí)現(xiàn)了按需加載或者惰性加載。
- JavaScript設(shè)計(jì)模式之單體模式全面解析
- javascript 單例/單體模式(Singleton)
- javascript 設(shè)計(jì)模式之單體模式 面向?qū)ο髮W(xué)習(xí)基礎(chǔ)
- javascript設(shè)計(jì)模式之模塊模式學(xué)習(xí)筆記
- javascript設(shè)計(jì)模式之策略模式學(xué)習(xí)筆記
- JavaScript 設(shè)計(jì)模式 安全沙箱模式
- javascript設(shè)計(jì)模式 接口介紹
- 小議javascript 設(shè)計(jì)模式 推薦
- JavaScript設(shè)計(jì)模式之觀察者模式(發(fā)布者-訂閱者模式)
- javascript設(shè)計(jì)模式之單體模式學(xué)習(xí)筆記
相關(guān)文章
onsubmit阻止form表單提交與onclick的相關(guān)操作
return false會阻止表單提交,基本上關(guān)于onsubmit=return false有以下幾點(diǎn)要注意的地方,學(xué)習(xí)后臺編程的朋友一定要知道。2010-09-09使用JavaScript在html文檔內(nèi)添加新的元素節(jié)點(diǎn)
這篇文章主要介紹了使用JavaScript在html文檔內(nèi)添加新的元素節(jié),主要打方式就是動態(tài)的改變原有html文檔結(jié)構(gòu),下文詳細(xì)介紹內(nèi)容需要的可以參考一下2022-02-02JS實(shí)現(xiàn)放大、縮小及拖拽圖片的方法【可兼容IE、火狐】
這篇文章主要介紹了JS實(shí)現(xiàn)放大、縮小及拖拽圖片的方法,可兼容IE及火狐等瀏覽器,通過javascript自定義函數(shù)實(shí)現(xiàn)針對圖片的放大、縮小及拖拽等功能,涉及javascript動態(tài)操作頁面元素的相關(guān)技巧,需要的朋友可以參考下2016-08-08js使用for循環(huán)及if語句判斷多個(gè)一樣的name
這篇文章主要介紹了js使用for循環(huán)機(jī)if語句判斷多個(gè)一樣的name,此法比較實(shí)用,需要的朋友可以參考下2014-09-09