這篇文章要跟大家分享的是JavaScript設計模式系列二:單例模式,有興趣的朋友可以看一下
前言:本系列程式碼已上傳到GitHub位址https://github.com/HolyZheng/...
單例模式
的定義:一個類別只有一個實例,並且可以在全域存取
。
什麼時候需要用到單例模式呢?其實單例模式在日常開發中的使用非常的廣泛,例如各種浮窗、像登入浮窗等,無論我們點擊多少次,都是同一個浮窗,浮窗從始至終只創建了一次。這種場景就十分適合運用單例模式。
我們創建一個「最老的人」的類,很明顯,「最老的人」有且只有一個。這很符合我們單例模式的運用場景。我們先來看看完整程式碼:
var oldestMan = function (name) { this.name = name; } oldestMan.prototype.getName = function () { console.log(this.name); } //引入一个代理函数和闭包的概念 var createOldestMan = (function () { var instance; return function (name) { if (!instance) { instance = new oldestMan(name); } return instance; } })(); var personA = createOldestMan("holz"); var personB = createOldestMan("Amy"); personA.getName(); // holz personB.getName(); // holz
我們可以在控制台上看到即使呼叫了兩次createOldestMan
並且賦了不一樣的值,但兩次getName ()
輸出的都是第一次的「holz」。這就是單例模式。
程式碼看不太懂?沒關係,現在給大家一一講解。
首先我們建立了一個oldestMan類,建立了一個name屬性。然後我們透過 prototype
給它加一個getName()方法用來取得oldestMan的名字,相信到這裡大家都是懂的,然後下面一段程式碼就是重點了,也比較難理解。我們打這段程式碼單獨拿出來將一下。
//引入一个代理函数和闭包的概念 var createOldestMan = (function () { var instance; return function (name) { if (!instance) { instance = new oldestMan(name); } return instance; } })();
首先,我們不用管什麼是代理函數,之所以叫它代理函數是因為它輔助我們實現單例模式的效果,這段函數第一個關鍵點是createOldestMan()
是一個立即執行函數。立即函數在宣告的時候就會立即執行,也就是在宣告createOldestMan的時候這個函數就會執行,它會宣告一個instance 變量,然後傳回一個函式給createOldestMan。 createOldestMan就相當於:
var createOldestMan = function (name) { if (!instance) { instance = new oldestMan(name); } return instance; }
第二個關鍵點是:這裡利用了 閉包
的概念。
閉包是什麼呢?我只需要記住當函數在定義時的語法作用域之外被調用,卻還能訪問定義時的語法作用域時,就是產生了閉包。
我們來看我們的程式碼,函數先定義了一個instance,然後再回傳一個function(name),而這個function(name)裡面用到了instance變數。在正常情況下,在立即執行函數執行之後,instance變數就會被JavaScript的垃圾回收機制回收,但是因為function(name)被返回到了外部,而function(name)隨時會被調用,隨時會訪問到instance變量,所以instance變數被保留在了記憶體中。這就產生了閉包。也就是說,function(name)被賦值給了外部的createOldestMan,在外部的語法作用域中執行,但還可以存取到定義時內部的語法作用域中的instance。
所以在 立即執行函數
和 閉包
的作用下,instance只申請了一次,也就是只有一個instance。也就是說,我們無論執行多少次createOldestMan("..."),instance只會是第一次的那個值。所以我們就可以判斷instance是否已經被實例化了,來給instance賦值,如果instance已經被實例化,就回傳instance。這就達到了一個類別只有一個實例的效果。
我還可以改造程式碼,因為在開發中,我們可能不只一個單例,所以我們應該讓程式碼變得各個單例通用。我們該在哪裡改呢?沒錯,改代理函數。我們只需要把代理函數中的oldestMan()提取出來,改為以參數的形式傳值,不限於oldestMan()。
var singleObj; var createSingleton = function (fn) { return function (text) { if (!singleObj) { singleObj = new fn (text); } return singleObj; } }
這樣我們就可以把單例當作參數傳進去,用它實現不同的單例了。
完整程式碼是這樣的:
var oldestMan = function (name) { this.name = name; } oldestMan.prototype.getName = function () { console.log(this.name); } //一个通用的代理函数 var singleObj; var createSingleton = function (fn) { return function (text) { if (!singleObj) { singleObj = new fn (text); } return singleObj; } } var person_1 = createSingleton(oldestMan)("holz"); var person_2 = createSingleton(oldestMan)("tom"); person_1.getName(); //holz person_2.getName(); //holz
同樣,即使再次呼叫createSingleton並傳入不同的值,輸出的依舊是第一次的「holz」。
單例模式的定義:一個類別只有一個實例,並且可以在全域存取。
適用場景:其實單例模式在日常開發中的使用非常的廣泛,例如各種浮窗、像登錄浮窗等,無論我們點擊多少次,都是同一個浮窗,浮窗從始至終只創建了一次。這種場景就十分適合運用單例模式。
相關推薦:
以上是JavaScript設計模式系列二:單例模式的詳細內容。更多資訊請關注PHP中文網其他相關文章!