摘要:雖然是弱類型的語言,但是也有構(gòu)造函數(shù)和實例。也就是說,我們只在第一次調(diào)用構(gòu)造函數(shù)時創(chuàng)建新對象,之后調(diào)用返回時返回該對象即可。而我不認為這是一個單例模式的原因如下我覺得既然兩次調(diào)用同一個構(gòu)造函數(shù),返回的不是同一個對象,那不就不能成為單例模式。
寫在前面
(度過一陣的繁忙期,又可以愉快的開始學習新知識了,一年來技術(shù)棧切來切去,卻總覺得js都還沒學完-_-)
本文主要圍繞js的設(shè)計模式進行展開,對每個設(shè)計模式從特征,原理和實現(xiàn)方式幾個方面進行說明。由于內(nèi)容較長,所以拆分成多篇文章。如果有不對的地方歡迎指出,閱讀前請注意幾點:
如果對js的類式繼承和閉包不太熟練的建議先閱讀相關(guān)內(nèi)容,比如我前面寫過的js繼承(主要看到原型鏈繼承部分就好)和js閉包,,
知識密度較大,建議邊思考,順便跑以下相關(guān)的代碼(如果碰到代碼有問題的歡迎指出),中途注意休息
正文 定義也叫單體模式,核心思想是確保一個類只對應(yīng)一個實例。
雖然js是弱類型的語言,但是js也有構(gòu)造函數(shù)和實例。所以這里可以理解為確保多次構(gòu)造函數(shù)時,都返回同一個實例
根據(jù)定義,我們需要實現(xiàn)一個構(gòu)造函數(shù),并且滿足以下條件:
function A(){ //需要實現(xiàn)的函數(shù)內(nèi)容 } var a1 = new A() var b1 = new A() a1 ==== b1 //true
在前面我們說到了構(gòu)造函數(shù)和實例,并且也知道了引用類型的值賦值的時候存放的實際是變量的地址指針,所以要實現(xiàn)這個構(gòu)造函數(shù)的核心思路是:每次調(diào)用構(gòu)造函數(shù)時,返回指向同一個對象的指針。 也就是說,我們只在第一次調(diào)用構(gòu)造函數(shù)時創(chuàng)建新對象,之后調(diào)用返回時返回該對象即可。所以重點變成了--如何緩存初次創(chuàng)建的變量對象。
首先先排除全局變量,因為一般情況下需要保證全局環(huán)境的純凈,其次全局變量容易被改寫,出現(xiàn)意外情況。所以采用以下2種方案來實現(xiàn)緩存。
1. 使用構(gòu)造函數(shù)的靜態(tài)屬性因為構(gòu)造函數(shù)本身也是對象,可以擁有靜態(tài)屬性。所以可以這樣實現(xiàn):
function A(name){ // 如果已存在對應(yīng)的實例 if(typeof A.instance === "object"){ return A.instance } //否則正常創(chuàng)建實例 this.name = name // 緩存 A.instance =this return this } var a1 = new A() var a2= new A() console.log(a1 === a2)//true
這種方法的缺點在于靜態(tài)屬性是能夠被人為重寫的,不過不會像全局變量那樣被無意修改。
2. 借助閉包通過閉包的方式來實現(xiàn)的核心思路是,當對象第一次被創(chuàng)建以后,重寫構(gòu)造函數(shù),在重寫后的構(gòu)造函數(shù)里面訪問私有變量。
function A(name){ var instance = this this.name = name //重寫構(gòu)造函數(shù) A = function (){ return instance } } var a1 = new A() var a2= new A() console.log(a1 === a2)//true
到這里我們其實已經(jīng)實現(xiàn)了最核心的步驟,但是這樣的實現(xiàn)存在問題,如果看過原型鏈繼承的小伙伴會注意到,如果我們在第一次調(diào)用構(gòu)造函數(shù)之后,由于構(gòu)造函數(shù)被重寫,那么在之后添加屬性和方法到A的原型上,就會丟失。比如:
function A(name){ var instance = this this.name = name //重寫構(gòu)造函數(shù) A = function (){ return instance } } A.prototype.pro1 = "from protptype1" var a1 = new A() A.prototype.pro2 = "from protptype2" var a2= new A() console.log(a1.pro1)//from protptype1 console.log(a1.pro2)//underfined console.log(a2.pro1)//from protptype1 console.log(a2.pro2)//underfined
重寫構(gòu)造函數(shù)之后,,實際上原先的A指針對應(yīng)的函數(shù)實際上還在內(nèi)存中(因為instance變量還在被引用著,這里的內(nèi)容如果忘記了請看閉包),但是此時A指針已經(jīng)指向了一個新的函數(shù)了,可以簡單測試下:
console.log(a1.constructor ==== A)//false
所以接下來我們應(yīng)該解決這個問題,根據(jù)上文可知,我們的重點是,調(diào)整原型實例之間的關(guān)系,所以應(yīng)該這樣實現(xiàn)(這一塊忘記的還是建議回頭看看js繼承里面的那張函數(shù)、原型、實例之間的關(guān)系圖點擊直達):
function A(name){ var instance = this this.name = name //重寫構(gòu)造函數(shù) A = function (){ return instance } // 第一種寫法,這里實際上實現(xiàn)了一次原型鏈繼承,如果不想這樣實現(xiàn),也可以直接指向原來的原型 A.prototype = this // 第二種寫法,直接指向舊的原型 A.prototype = this.constructor.prototype instance = new A() // 調(diào)整構(gòu)造函數(shù)指針,這里實際上實現(xiàn)了一次原型鏈繼承,如果不想這樣實現(xiàn),也可以直接指向原來的原型 instance.constructor = A return instance } A.prototype.pro1 = "from protptype1" var a1 = new A() A.prototype.pro2 = "from protptype2" var a2= new A() console.log(a1.pro1)//from protptype1 console.log(a1.pro2)//from protptype2 console.log(a2.pro1)//from protptype1 console.log(a2.pro2)//from protptype2
現(xiàn)在一切就正常了。還有一種方式,是利用立即執(zhí)行函數(shù)來保持私有變量,(立即執(zhí)行函數(shù)的內(nèi)容請看《詳解js中的函數(shù)部分》)原理也是閉包:
var A; (function(name){ var instance; A = function(name){ if(instance){ return instance } //賦值給私有變量 instance = this //自身屬性 this.name = name } }()); A.prototype.pro1 = "from protptype1" var a1 = new A("a1") A.prototype.pro2 = "from protptype2" var a2 = new A("a2") console.log(a1.name) console.log(a1.pro1)//from protptype1 console.log(a1.pro2)//from protptype2 console.log(a2.pro1)//from protptype1 console.log(a2.pro2)//from protptype2
簡單說明一下上面的內(nèi)容,首先利用在立即執(zhí)行函數(shù)中保存一個私有變量instance,初次執(zhí)行之后,第一次調(diào)用new A()之后,生成一個對象并讓instance指向該對象,從第二次開始,調(diào)用new A(),都只返回這個對象,
*特殊情況很多地方會提到,使用字面量直接創(chuàng)建一個對象也是一個單例模式的實例。這個說法我個人覺得并不夠嚴格,和同事探討之后覺得可能是這樣(如果有有其他見解的小伙伴歡迎指出):使用字面量寫法的時候,實際上相當于使用原生的Object函數(shù)new了一個對象,然后存儲到內(nèi)存里,之后我們每次使用對應(yīng)的指針去讀取時,讀到的都是這個對象。而我不認為這是一個單例模式的原因如下:
var obj1 = new Object({ name:111 }) var obj2 = new Object({ name:111 }) console.log(obj1===obj2)//false
我覺得既然兩次調(diào)用同一個構(gòu)造函數(shù),返回的不是同一個對象,那不就不能成為單例模式。當然,這一部分是我個人的看法,讀者朋友還是要注意區(qū)分。
小結(jié)單例模式先說到這里,后面會陸續(xù)補充其他的設(shè)計模式。
感謝之前的熱心讀者,尤其是為我指出錯誤的小伙伴。
然后依然是每次都一樣的結(jié)尾,如果內(nèi)容有錯誤的地方歡迎指出;如果對你有幫助,歡迎點贊和收藏,轉(zhuǎn)載請征得同意后著明出處,如果有問題也歡迎私信交流,主頁添加了郵箱地址~溜了
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/95699.html
摘要:什么是設(shè)計模式設(shè)計模式是一種能夠被反復(fù)使用,符合面向?qū)ο筇匦缘拇a設(shè)計經(jīng)驗的總結(jié),合理的使用設(shè)計模式能夠讓你得代碼更容易維護和可靠設(shè)計模式的類型共分為創(chuàng)建型模式,結(jié)構(gòu)型模式,行為型模式三種創(chuàng)建型模式創(chuàng)建型模式是對一個類的實例化過程進行了抽象 什么是設(shè)計模式 設(shè)計模式是一種能夠被反復(fù)使用,符合面向?qū)ο筇匦缘拇a設(shè)計經(jīng)驗的總結(jié),合理的使用設(shè)計模式能夠讓你得代碼更容易維護和可靠設(shè)計模式的類型...
摘要:什么是單例模式單例模式是一種十分常用但卻相對而言比較簡單的單例模式。對象就是單例模式的體現(xiàn)??偨Y(jié)單例模式雖然簡單,但是在項目中的應(yīng)用場景卻是相當多的,單例模式的核心是確保只有一個實例,并提供全局訪問。 1. 什么是單例模式? 單例模式是一種十分常用但卻相對而言比較簡單的單例模式。它是指在一個類只能有一個實例,即使多次實例化該類,也只返回第一次實例化后的實例對象。單例模式不僅能減少不必要...
摘要:單例模式是一個用來劃分命名空間并將一批屬性和方法組織在一起的對象,如果它可以被實例化,那么它只能被實例化一次。 單例模式是一個用來劃分命名空間并將一批屬性和方法組織在一起的對象,如果它可以被實例化,那么它只能被實例化一次。 原文鏈接 單例模式優(yōu)點 劃分命名空間,減少全局變量 組織代碼為一體,便于閱讀維護 并非所有的對象字面量都是單例,比如模擬數(shù)據(jù) 基本結(jié)構(gòu): let Cat = {...
摘要:以上,吐槽完畢設(shè)計模式主要分為三大類創(chuàng)建型模式結(jié)構(gòu)型模式行為模式。單例模式所謂單例模式,是指僅實例化該類一次,該實例提供一個眾所周知的全局訪問點。單例模式暫時這么些,如果后續(xù)了解更多會默默補充上來,下一次會分享工廠模式。 初入js坑時,滿臉懵逼的我認為設(shè)計模式就該是后端頭疼的,對,頭疼,蓋因粗略掃描下來也就十幾二十種吧,彼時頗有種隔岸觀火看到你過得不好我也就安心的自得。染鵝,打臉啪啪的...
閱讀 1833·2021-11-18 13:21
閱讀 1966·2021-10-18 13:30
閱讀 1551·2021-10-12 10:13
閱讀 922·2021-10-09 09:43
閱讀 5436·2021-09-22 15:13
閱讀 3595·2021-08-11 10:22
閱讀 947·2019-08-30 13:46
閱讀 3527·2019-08-30 13:21