這是關於JavaScript設計模式系列文章的第一篇。1995年,Erich Gamma, Richard Helm, Ralph Johnson和John Vlissides(有名的四人組,譯註:The Gang of Four, 後文統稱GoF)出版了《設計模式:可複用面向對象軟件的基礎》,一本爲軟件架構和設計難題提供一系列解決方案的書。書中同時爲這些解決方案(譯註:即各類設計模式)提供了一份詞彙參照。若是你感興趣能夠在維基百科上了解更多。
那本書裏對於每種解決方案的實現是用C++和Smalltalk寫的,它們與JavaScript截然不同。另外一本書《Pro JavaScript Design Patterns》則是用JavaScript來實現那些設計模式的。我本但願從這本書裏能獲得不少知識,然而事與願違……他們可能只是想調起你的胃口讓你去買這本書罷了。若是你真的買了這本書必定要讓他們知道是我推薦你買的。或許他們會給我一些補償(也可能不會,可是至少我是這麼但願的)。javascript
在JavaScript裏,單例模式(the Singleton Pattern)很是簡單,簡單到能夠被忽略,可是它在技術層面是如何工做的,咱們仍是有必要了解一下的。單例的代碼寫在一個單獨的對象裏,所以你不須要去實例化一個新對象就能夠在任何你須要的時候使用它的資源,單例容許在全局範圍內訪問它的資源。
在JavaScript裏,咱們常在管理命名空間時使用單例模式,它能夠下降你在代碼中建立全局變量的數量。單例模式在JavaScript裏要比在其餘語言中更有用,由於它能夠用命名空間來下降全局變量所帶來的風險。php
這是一個最基本最簡單的用JavaScript實現的單例模式。它就是一個簡單的有一些方法和屬性的對象字面量,假想它們是由於某種關係才被放到一塊兒。java
var Singleton = { attr: 1, another_attr: 'value', method: function() {...}, another_method: function() {...} };
由於它是一個對象字面量,因此我不須要實例化它,而且這個對象僅此一個。也就是說咱們能夠經過一個全局變量的入口訪問全部的方法和屬性,以下所示:ajax
Singleton.attr += 1;
Singleton.method();
...
單例模式在JavaScript中的一種使用情形就建立命名空間。像Java和C#這樣的語言,命名空間這種特性已經被內建到語言自己了,而且命名空間在這些語言中是必須的。經過建立命名空間或者包來把代碼組織到邏輯塊中。這也是在JavaScript中使用單例模式最重要的緣由,當你經過使用命名空間把你的代碼從全局做用域移動到一個新的單例中的時候,能夠避免全局變量被意外覆蓋(overwrite)以及全局變量引發其餘的bug。
使用單例模式管理命名空間很是簡單。一樣你能夠僅僅經過建立一個對象字面量就搞定:設計模式
var Namespace = { Util: { util_method1: function() {...}, util_method2: function() {...} }, Ajax: { ajax_method: function() {...} }, some_method: function() {...} };
如你所見,如今若是你想使用一個實用方法,能夠在Namespace.Util
下面找到它,就像下面代碼中所示的那樣。固然,下面所示的some_method
方法並無在單例中嵌套多層。閉包
Namespace.Util.util_method1();
Namespace.Ajax.ajax_method();
Namespace.some_method();
一般你有可能會把這些方法做爲全局函數,這也就意味着它們很是有可能會被覆蓋,尤爲是像get
這樣簡單的名字,諸如此類的名字再日常不過了。你只須要把所有的變量和函數添加到一個單例中,就能夠徹底不給別人篡改你代碼的機會。架構
在不少狀況下,一個網站的某些頁面運行的JavaScript代碼和其餘頁面是不一樣的。你能夠用單例管理命名空間的技術來爲每一個頁面組織專門的代碼,而後在頁面加載完成後執行它們。ide
Namespace.homepage = { init: function() {...}, method1: function() {...}, method2: function() {...} } Namespace.contactpage = { init: function() {...}, method1: function() {...}, method2: function() {...} } Namespace.pageutil = { getPageName: function() { // 返回當前頁面的標識符 } } var pageName = Namespace.pageutil.getPageName(); window.onload = Namespace[pageName].init;
這對於針對不一樣頁面上出現的不一樣表單添加驗證代碼很是有用。你甚至能夠提取出一個新的命名空間來封裝些實用功能,好比我這裏的Namespace.pageutil.getPageName
。這和我以前提到的稍有不一樣,由於getPageName
方法並不是某個頁面專有的代碼,可是卻能夠用它來正確映射到頁面專有代碼。函數
《Pro JavaScript Design Patterns》這本書中對單例模式介紹了更多,然而事實上我把書中整整6頁的內容壓縮成這篇短小的博客,書中也說起了經過閉包,被動初始化和分支建立私有變量。就像我在文章開頭所說,關於這本書我不想複製太多的內容過來,只是想勾起你的興趣讓你去買它。這麼作的好處是,你既能夠給他們經濟援助,同時也能避免我讓人家給告了。更況且,一篇博客文章不該該硬塞下一本書中一章的內容。網站
若是你認爲本文對你有所幫助或者你只是純粹地喜歡這篇文章,請用文章下方的社交分享按鈕把它傳播出去。我這種從後山來的歷來不敢想像能有人像你這樣幫我。謝謝!
請繼續關注JavaScript設計模式系列的更多文章:
– 單例模式(Singleton Pattern)
– 橋接模式(Bridge Pattern)
– 組合模式(Composite Pattern)
– 外觀模式(Facade Pattern)
– 適配器模式(Adapter Pattern)
– 裝飾者模式(Decorator Pattern)
– 工廠模式(一)(Factory Pattern Part 1)
– 工廠模式(二)(Factory Pattern Part 2)
– 代理模式(Proxy Pattern)
– 觀察者模式(Observer Pattern)
– 命令模式(Command Pattern)
– 責任鏈模式(Chain of Responsibility Pattern)
轉載請註明:
英文做者:Joe Zim
英文原文:http://www.joezimjs.com/javascript/javascript-design-patterns-singleton/
中文譯者:David @CodingSerf
中文譯文:http://www.codingserf.com/index.php/2015/05/javascript-design-patterns-singleton/