摘要:實(shí)現(xiàn)代碼如下創(chuàng)建元素設(shè)置樣式我們發(fā)現(xiàn)在開發(fā)中并不會(huì)多帶帶使用遮罩層,遮罩層和彈出窗是經(jīng)常結(jié)合在一起使用,前面我們提到過(guò)登陸彈窗使用單例模式實(shí)現(xiàn)也是最適合的。
定義
確保一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)訪問(wèn)它的全局訪問(wèn)點(diǎn)。
單例模式使用的場(chǎng)景比如線程池、全局緩存等。我們所熟知的瀏覽器的window對(duì)象就是一個(gè)單例,在JavaScript開發(fā)中,對(duì)于這種只需要一個(gè)的對(duì)象,我們的實(shí)現(xiàn)往往使用單例。
實(shí)現(xiàn)單例模式 (不透明的)一般我們是這樣實(shí)現(xiàn)單例的,用一個(gè)變量來(lái)標(biāo)志當(dāng)前的類已經(jīng)創(chuàng)建過(guò)對(duì)象,如果下次獲取當(dāng)前類的實(shí)例時(shí),直接返回之前創(chuàng)建的對(duì)象即可。代碼如下:
// 定義一個(gè)類 function Singleton(name) { this.name = name; this.instance = null; } // 原型擴(kuò)展類的一個(gè)方法getName() Singleton.prototype.getName = function() { console.log(this.name) }; // 獲取類的實(shí)例 Singleton.getInstance = function(name) { if(!this.instance) { this.instance = new Singleton(name); } return this.instance }; // 獲取對(duì)象1 var a = Singleton.getInstance("a"); // 獲取對(duì)象2 var b = Singleton.getInstance("b"); // 進(jìn)行比較 console.log(a === b);
我們也可以使用閉包來(lái)實(shí)現(xiàn):
function Singleton(name) { this.name = name; } // 原型擴(kuò)展類的一個(gè)方法getName() Singleton.prototype.getName = function() { console.log(this.name) }; // 獲取類的實(shí)例 Singleton.getInstance = (function() { var instance = null; return function(name) { if(!this.instance) { this.instance = new Singleton(name); } return this.instance } })(); // 獲取對(duì)象1 var a = Singleton.getInstance("a"); // 獲取對(duì)象2 var b = Singleton.getInstance("b"); // 進(jìn)行比較 console.log(a === b);
這個(gè)單例實(shí)現(xiàn)獲取對(duì)象的方式經(jīng)常見于新手的寫法,這種方式獲取對(duì)象雖然簡(jiǎn)單,但是這種實(shí)現(xiàn)方式不透明。知道的人可以通過(guò) Singleton.getInstance() 獲取對(duì)象,不知道的需要研究代碼的實(shí)現(xiàn),這樣不好。這與我們常見的用 new 關(guān)鍵字來(lái)獲取對(duì)象有出入,實(shí)際意義不大。
實(shí)現(xiàn)單例模式 (透明的)var Singleton = (function(){ var instance; var CreateSingleton = function (name) { this.name = name; if(instance) { return instance; } // 打印實(shí)例名字 this.getName(); // instance = this; // return instance; return instance = this; } // 獲取實(shí)例的名字 CreateSingleton.prototype.getName = function() { console.log(this.name) } return CreateSingleton; })(); // 創(chuàng)建實(shí)例對(duì)象1 var a = new Singleton("a"); // 創(chuàng)建實(shí)例對(duì)象2 var b = new Singleton("b"); console.log(a===b);
這種單例模式我以前用過(guò)一次,但是使用起來(lái)很別扭,我也見過(guò)別人用這種方式實(shí)現(xiàn)過(guò)走馬燈的效果,因?yàn)樽唏R燈在我們的應(yīng)用中絕大多數(shù)只有一個(gè)。
這里先說(shuō)一下為什么感覺(jué)不對(duì)勁,因?yàn)樵谶@個(gè)單例的構(gòu)造函數(shù)中一共干了兩件事,一個(gè)是創(chuàng)建對(duì)象并打印實(shí)例名字,另一個(gè)是保證只有一個(gè)實(shí)例對(duì)象。這樣代碼量大的化不方便管理,應(yīng)該盡量做到職責(zé)單一。
我們通常會(huì)將代碼改成下面這個(gè)樣子:
// 單例構(gòu)造函數(shù) function CreateSingleton (name) { this.name = name; this.getName(); }; // 獲取實(shí)例的名字 CreateSingleton.prototype.getName = function() { console.log(this.name) }; // 單例對(duì)象 var Singleton = (function(){ var instance; return function (name) { if(!instance) { instance = new CreateSingleton(name); } return instance; } })(); // 創(chuàng)建實(shí)例對(duì)象1 var a = new Singleton("a"); // 創(chuàng)建實(shí)例對(duì)象2 var b = new Singleton("b"); console.log(a===b);
這種實(shí)現(xiàn)方式我們就比較熟悉了,我們?cè)陂_發(fā)中經(jīng)常會(huì)使用中間類,通過(guò)它來(lái)實(shí)現(xiàn)原類所不具有的特殊功能。有的人把這種實(shí)現(xiàn)方式叫做代理,這的確是單例模式的一種應(yīng)用,稍后將在代理模式進(jìn)行詳解。
說(shuō)了這么多我們還是在圍繞著傳統(tǒng)的單例模式實(shí)現(xiàn)在進(jìn)行講解,那么具有JavaScript特色的單例模式是什么呢。
JavaScript單例模式在我們的開發(fā)中,很多同學(xué)可能并不知道單例到底是什么,應(yīng)該如何使用單例,但是他們所寫的代碼卻剛好滿足了單例模式的要求。如要實(shí)現(xiàn)一個(gè)登陸彈窗,不管那個(gè)頁(yè)面或者在頁(yè)面的那個(gè)地方單擊登陸按鈕,都會(huì)彈出登錄窗。一些同學(xué)就會(huì)寫一個(gè)全局的對(duì)象來(lái)實(shí)現(xiàn)登陸窗口功能,是的,這樣的確可以實(shí)現(xiàn)所要求的登陸效果,也符合單例模式的要求,但是這種實(shí)現(xiàn)其實(shí)是一個(gè)巧合,或者一個(gè)美麗的錯(cuò)誤。由于全局對(duì)象,或者說(shuō)全局變量正好符合單例的能夠全局訪問(wèn),而且是唯一的。但是我們都知道,全局變量是可以被覆蓋的,特別是對(duì)于初級(jí)開發(fā)人員來(lái)說(shuō),剛開始不管定義什么基本都是全局的,這樣的好處是方便訪問(wèn),壞處是一不留意就會(huì)引起沖突,特別是在做一個(gè)團(tuán)隊(duì)合作的大項(xiàng)目時(shí),所以成熟的有經(jīng)驗(yàn)的開發(fā)人員盡量減少全局的聲明。
而在開發(fā)中我們避免全局變量污染的通常做法如下:
全局命名空間
使用閉包
它們的共同點(diǎn)是都可以定義自己的成員、存儲(chǔ)數(shù)據(jù)。區(qū)別是全局命名空間的所有方法和屬性都是公共的,而閉包可以實(shí)現(xiàn)方法和屬性的私有化。
惰性單例模式說(shuō)實(shí)話,在我下決心學(xué)習(xí)設(shè)計(jì)模式之前我并不知道,單例模式還分惰性單例模式,直到我看了曾探大神的《JvaScript設(shè)計(jì)模式與開發(fā)實(shí)踐》后才知道了還有惰性單例模式,那么什么是惰性單例模式呢?在說(shuō)惰性單例模式之前,請(qǐng)?jiān)试S我先說(shuō)一個(gè)我們都知道的lazyload加載圖片,它就是惰性加載,只當(dāng)含有圖片資源的dom元素出現(xiàn)在媒體設(shè)備的可視區(qū)時(shí),圖片資源才會(huì)被加載,這種加載模式就是惰性加載;還有就是下拉刷新資源也是惰性加載,當(dāng)你觸發(fā)下拉刷新事件資源才會(huì)被加載等。而惰性單例模式的原理也是這樣的,只有當(dāng)觸發(fā)創(chuàng)建實(shí)例對(duì)象時(shí),實(shí)例對(duì)象才會(huì)被創(chuàng)建。這樣的實(shí)例對(duì)象創(chuàng)建方式在開發(fā)中很有必要的。
就如同我們剛開始介紹的用 Singleton.getInstance 創(chuàng)建實(shí)例對(duì)象一樣,雖然這種方式實(shí)現(xiàn)了惰性單例,但是正如我們剛開始說(shuō)的那樣這并不是一個(gè)好的實(shí)現(xiàn)方式。下面就來(lái)介紹一個(gè)好的實(shí)現(xiàn)方式。
遮罩層相信大家對(duì)它都不陌生。它在開發(fā)中比較常見,實(shí)現(xiàn)起來(lái)也比較簡(jiǎn)單。在每個(gè)人的開發(fā)中實(shí)現(xiàn)的方式不盡相同。這個(gè)最好的實(shí)現(xiàn)方式還是用單例模式。有的人實(shí)現(xiàn)直接在頁(yè)面中加入一個(gè)div然后設(shè)置display為none,這樣不管我們是否使用遮罩層頁(yè)面都會(huì)加載這個(gè)div,如果是多個(gè)頁(yè)面就是多個(gè)div的開銷;也有的人使用js創(chuàng)建一個(gè)div,當(dāng)需要時(shí)就用將其加入到body中,如果不需要就刪除,這樣頻繁地操作dom對(duì)頁(yè)面的性能也是一種消耗;還有的人是在前一種的基礎(chǔ)上用一個(gè)標(biāo)識(shí)符來(lái)判斷,當(dāng)遮罩層是第一次出現(xiàn)就向頁(yè)面添加,不需要時(shí)隱藏,如果不是就是用前一次的添加的。
實(shí)現(xiàn)代碼如下:
// html // js var createMask = (function() { var mask; return function() { if(!mask) { // 創(chuàng)建div元素 var mask = document.createElement("div"); // 設(shè)置樣式 mask.style.position = "fixed"; mask.style.top = "0"; mask.style.right = "0"; mask.style.bottom = "0"; mask.style.left = "0"; mask.style.opacity = ""; mask.style.display = "none"; document.body.appendChild(mask); } return mask; } })(); document.getElementById("btn").onclick = function() { var maskLayer = createMask(); maskLayer.style.display = "block"; }
我們發(fā)現(xiàn)在開發(fā)中并不會(huì)多帶帶使用遮罩層,遮罩層和彈出窗是經(jīng)常結(jié)合在一起使用,前面我們提到過(guò)登陸彈窗使用單例模式實(shí)現(xiàn)也是最適合的。那么我們是不是要將上面的代碼拷貝一份呢?當(dāng)然我們還有好的實(shí)現(xiàn)方式,那就是將上面單例中代碼變化的部分和不變的部分,分離開來(lái)。
代碼如下:
var singleton = function(fn) { var instance; return function() { return instance || (instance = fn.apply(this, arguments)); } }; // 創(chuàng)建遮罩層 var createMask = function(){ // 創(chuàng)建div元素 var mask = document.createElement("div"); // 設(shè)置樣式 mask.style.position = "fixed"; mask.style.top = "0"; mask.style.right = "0"; mask.style.bottom = "0"; mask.style.left = "0"; mask.style.opacity = "o.75"; mask.style.backgroundColor = "#000"; mask.style.display = "none"; mask.style.zIndex = "98"; document.body.appendChild(mask); // 單擊隱藏遮罩層 mask.onclick = function(){ this.style.display = "none"; } return mask; }; // 創(chuàng)建登陸窗口 var createLogin = function() { // 創(chuàng)建div元素 var login = document.createElement("div"); // 設(shè)置樣式 login.style.position = "fixed"; login.style.top = "50%"; login.style.left = "50%"; login.style.zIndex = "100"; login.style.display = "none"; login.style.padding = "50px 80px"; login.style.backgroundColor = "#fff"; login.style.border = "1px solid #ccc"; login.style.borderRadius = "6px"; login.innerHTML = "login it"; document.body.appendChild(login); return login; }; document.getElementById("btn").onclick = function() { var oMask = singleton(createMask)(); oMask.style.display = "block"; var oLogin = singleton(createLogin)(); oLogin.style.display = "block"; var w = parseInt(oLogin.clientWidth); var h = parseInt(oLogin.clientHeight); }
在上面的實(shí)現(xiàn)中將單例模式的惰性實(shí)現(xiàn)部分提取出來(lái),實(shí)現(xiàn)了惰性實(shí)現(xiàn)代碼的復(fù)用,其中使用apply改變改變了fn內(nèi)的this指向,使用 || 預(yù)算簡(jiǎn)化代碼的書寫。
設(shè)計(jì)模式相關(guān)文章
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/92501.html
摘要:但是,這并不是采用單例的唯一原因。使用命名空間單例模式也被稱為模塊設(shè)計(jì)模式。函數(shù)內(nèi)部聲明了一些局部函數(shù)和或變量。緊隨函數(shù)聲明放置即可立即執(zhí)行外部函數(shù),并將所得的對(duì)象文字費(fèi)賠給變量。 JavaScript設(shè)計(jì)模式-第一部分:?jiǎn)卫J?、組合模式和外觀模式 設(shè)計(jì)模式是一些可靠的編程方式,有助于保證代碼更加易于維護(hù)、擴(kuò)展及分離,所有設(shè)計(jì)模式在創(chuàng)建大型JavaScript應(yīng)用程序時(shí)均不可或缺 單...
摘要:此時(shí)我們創(chuàng)建的對(duì)象內(nèi)保存靜態(tài)變量通過(guò)取值器訪問(wèn),最后將這個(gè)對(duì)象作為一個(gè)單例放在全局空間里面作為靜態(tài)變量單例對(duì)象供他人使用。 單例模式 又被稱為單體模式,是只允許實(shí)例化一次的對(duì)象類。有時(shí)我們也用一個(gè)對(duì)象來(lái)規(guī)劃一個(gè)命名空間,井井有條的管理對(duì)象上面的屬性和方法。 傳統(tǒng)的面向?qū)ο笳Z(yǔ)言中單例模式的實(shí)現(xiàn),均是單例對(duì)象從類中創(chuàng)建而來(lái),在以類為中心的語(yǔ)言中,這是很常見的做法。如果需要某個(gè)對(duì)象,就必須先...
摘要:不符合設(shè)計(jì)模式中的單一職責(zé)的概念。引入代理實(shí)現(xiàn)單例模式引入代理實(shí)現(xiàn)單例模式的特點(diǎn)我們負(fù)責(zé)管理單例的邏輯移到了代理類中。的單例模式對(duì)比在以上的代碼中實(shí)現(xiàn)的單例模式都混入了傳統(tǒng)面向?qū)ο笳Z(yǔ)言的特點(diǎn)。 聲明:這個(gè)系列為閱讀《JavaScript設(shè)計(jì)模式與開發(fā)實(shí)踐》 ----曾探@著一書的讀書筆記 1.單例模式的特點(diǎn)和定義 保證一個(gè)類僅有一個(gè)實(shí)例,并且提供一個(gè)訪問(wèn)它的全局訪問(wèn)點(diǎn)。 2.傳統(tǒng)面向?qū)?..
摘要:停更許久,近期計(jì)劃更新設(shè)計(jì)模式系列。單例模式是創(chuàng)建型設(shè)計(jì)模式的一種。雖然它不是正規(guī)的單例模式,但不可否認(rèn)確實(shí)具備類單例模式的特點(diǎn)。適用場(chǎng)景單例模式的特點(diǎn),意圖解決維護(hù)一個(gè)全局實(shí)例對(duì)象。 停更許久,近期計(jì)劃更新:設(shè)計(jì)模式系列。 showImg(https://segmentfault.com/img/bVbt7uw?w=800&h=600); 單例模式:限制類實(shí)例化次數(shù)只能一次,一個(gè)類只...
摘要:觀察構(gòu)造函數(shù)的代碼,該構(gòu)造函數(shù)實(shí)際上負(fù)責(zé)了兩件事情第一是創(chuàng)建對(duì)象和執(zhí)行初始化方法,第二是保證只有一個(gè)對(duì)象。惰性單例在實(shí)際開發(fā)中非常有用,是單例模式的重點(diǎn)。 單例模式 單例模式的定義是: 保證一個(gè)類僅有一個(gè)實(shí)例,并提供一個(gè)訪問(wèn)它的全局訪問(wèn)點(diǎn)。 單例模式是一種常用的模式,有一些對(duì)象我們往往只需要一個(gè),比如線程池、全局緩存、瀏覽器的window對(duì)象等。例如,當(dāng)我們點(diǎn)擊登錄按鈕時(shí),頁(yè)面會(huì)彈出一...
摘要:在面向?qū)ο蟮恼Z(yǔ)言中,比如,等,單例模式通常是定義類時(shí)將構(gòu)造函數(shù)設(shè)為,保證對(duì)象不能在外部被出來(lái),同時(shí)給類定義一個(gè)靜態(tài)的方法,用來(lái)獲取或者創(chuàng)建這個(gè)唯一的實(shí)例。 萬(wàn)事開頭難,作為正經(jīng)歷菜鳥賽季的前端player,已經(jīng)忘記第一次告訴自己要寫一些東西出來(lái)是多久以的事情了。。。如果,你也和我一樣,那就像我一樣,從現(xiàn)在開始,從看到這篇文章開始,打開電腦,敲下你的第一篇文章(或者任何形式的文字)吧。 ...
閱讀 3550·2021-11-22 15:22
閱讀 3337·2019-08-30 15:54
閱讀 2732·2019-08-30 15:53
閱讀 822·2019-08-29 11:22
閱讀 3543·2019-08-29 11:14
閱讀 2084·2019-08-26 13:46
閱讀 2219·2019-08-26 13:24
閱讀 2283·2019-08-26 12:22