單例模式是javascript中最簡單也是最常用的模式之一。這種模式涉及到一個單一的類,該類負責創建自己的對象,同時確保只有單個對象被創建。這個類提供了一種訪問其唯一的對象的方式,可以直接訪問,不需要實例化該類的對象。 單例模式的特點: 1、單例類只能有一個實例。 2、單例類必須自己創建自... ...
單例模式是javascript中最簡單也是最常用的模式之一。這種模式涉及到一個單一的類,該類負責創建自己的對象,同時確保只有單個對象被創建。這個類提供了一種訪問其唯一的對象的方式,可以直接訪問,不需要實例化該類的對象。
單例模式的特點:
1、單例類只能有一個實例。
2、單例類必須自己創建自己的唯一實例。
3、單例類必須給所有其他對象提供這一實例。
主要解決:一個全局使用的類頻繁地創建與銷毀。
怎麼方便理解和記憶這種模式呢?
用一句話來記憶它就是:只有一個實例,有一個訪問它的全局訪問點,不能與new關鍵字一起使用。
那麼從最簡單的單例模式講起,在javascript中一個對象字面量可以認為是一個最簡單的單例類,以為它符合單例類的特點:只有一個實例,有一個全局訪問點。
示例:
var Singleton = { attribute: true, method1: function(){ //do something }, method2: function(){ //do something }, };
Singleton.name = false;
上面示例的單例對象可以被修改,你可以隨意添加屬性和方法到對象中,又或者用delete運算符刪除現有的成員。這實質上是違背了面向對象設計的一個原則:類可以被擴展,但不應該被修改。傳統意義上的單例模式的定義是:單例類僅有一個實例,並提供一個訪問它的全局訪問點。上面的對象字面量不是一個實例化的類,所以嚴格來說,它不屬於單例類。javascript不是一門傳統的語言,所以不必一定要按傳統的定義來限定它,我們將單例模式的定義更廣義化:單例類是一組相關的屬性和方法的集合,如果它能被實例化,那麼它只能被實例化一次。這樣對象字面量就符合單例模式的定義了。
擁有私有成員的單例類:
現在一個對象字面量就是javascript中最簡單的單例類,那怎麼實現單例類的私有成員呢?私有成員是對象內部獨有的、其他對象無法訪問的成員。在javascript中實現私有成員的方法是使用閉包:
var Singleton = (function(){ var name = 'singleton'; function getName(){ alert(name); }; return { attribute: true, getName: getName } })(); Singleton.getName(); //singleton
上面的單例類實現的name成員的私有化。
單例的使用很廣泛,一個最常見的例子就是網頁中的彈框層,比如:登錄框、提示框等等。
單例模式實現網頁彈框:
var createDiv = (function(){ div = document.createElement("div"); div.innerHTML = '我是登錄框'; document.body.appendChild(div); return div; })();
這種在頁面一開始就創建DOM節點的方式有一個問題,也許我們進入一個網站只是隨便看看,根本不需要進行登錄操作,因為登錄浮窗總是一開始就被創建好,那麼很有可能將白白浪費一些 DOM節點。
惰性單例
惰性單例指的是在需要的時候才創建對象實例。惰性單例是單例模式的重點,這種技術在實
際開發中非常有用。
接下來我們使用惰性單例實現彈框,在用戶點擊登錄按鈕的時候才創建登錄框:
var createDiv = function(){ var div;
return function(){ if(!div){ div = document.createElement("div"); div.innerHTML = '我是登錄框'; document.body.appendChild(div); } return div; } }; document.querySelector(".btn").onclick = createDiv();
通用的惰性單例
把上面創建div的過程提取出來,便得到一個通用的惰性單例模式:
var createSingleton = function(fn){ var singleton;
return function(){ return singleton || (singleton = fn.apply(this, arguments)); } };
現在用通用的惰性單例改造下前面登錄框的代碼:
var createSingleton = function(fn){ var singleton;
return function(){ return singleton || (singleton = fn.apply(this, arguments)); } }; function createDiv(){ var div = document.createElement("div"); div.innerHTML = '我是登錄框'; document.body.appendChild(div); return div; }; var createDivSingleton = createSingleton(createDiv); document.querySelector(".btn").onclick = function(){ var div = createDivSingleton(); };
以上登錄框應該一開始是隱藏狀態的,這裡為了能更好的理解單例模式,簡化了這些細節。
單例模式的優缺點:
優點: 1、在記憶體里只有一個實例,減少了記憶體的開銷,尤其是頻繁的創建和銷毀實例(比如管理學院首頁頁面緩存)。 2、避免對資源的多重占用(比如寫文件操作)。
缺點:沒有介面,不能繼承,與單一職責原則衝突,一個類應該只關心內部邏輯,而不關心外面怎麼樣來實例化。