單例模式
單例模式:又稱單體模式,是javascript中最有用最基本的模式。
應用場景:單例模式是一種常用的模式,有一些對象我們往往只需要一個,比如線程池、全局緩存、瀏覽器中的window對象等。在JavaScript開發中,單例模式的用途同樣非常廣泛。試想一下,當我們單擊登錄按鈕的時候,頁面中會出現一個登錄浮窗,而這個登錄浮窗是唯一的,無論單擊多少次登錄按鈕,這個浮窗都只會被創建一次,那麼這個登錄浮窗就適合用單例模式來創建。
單例模式實現的目的:確保只有一個實例,並提供全局訪問。
全局變量可以達到以上目的,但是存在很多問題,會造成命名空間污染。如果代碼中有很多變量,容易造成變量覆蓋等問題。這時,單例模式顯得尤爲重要。
實現單利模式的方法有如下幾種:
一、最簡單的字面量方法
//使用字面量方式
var mySingleton = {
property1: "something",
property2: "something else",
method1: function () {
console.log('hello world');
}
};
var t1 = mySingleton;
var t2 = mySingleton;
alert( t1.property1);//something
t1.method1();//hello world
alert(t1===t2);//true
字面量實現單例模式的優點是簡單實用,減少了變量和全局變量打交道的機會;但缺點也很明顯,屬性和方法都是暴露的,缺少封裝性。對於一些需要使用私有變量的情況時,顯然是不適用的。
二、構造函數內部判斷
//構造函數內部判斷方式
function Construct(){
if(Construct.unique!==undefined){
return Construct.unique;
}
this.property1 = 'something';
this.property2 = 'something else';
this.method1 = function (){
console.log('hello world');
}
Construct.unique = this;
}
var t1 = new Construct();
var t2 = new Construct();
alert(t1.property1);
alert(t2.property1);
t1.method1();
t2.method1();
alert(t1===t2);
用構造函數的方法實現單例模式,相當於創造了一個單例類,從單例類中創造對象,可以像其他普通類一樣使用。但是這種方式也沒有安全性,一旦我在外部修改了Construct的unique屬性,那麼單例模式也就被破壞了。
三、閉包方法(使用閉包封裝私有變量)
這種方法把一些變量封裝在閉包內部,只暴露一些接口跟外界通信。
var mySingleton = (function(){
var unique;
function Construct(){
// ... 生成單例的構造函數的代碼
if(Construct.unique!==undefined){
return Construct.unique;
}
this.property1 = 'something';
this.property2 = 'something else';
this.method1 = function (){
console.log('hello world');
}
Construct.unique = this;
}
unique = new Construct();
/*公有變量和方法 */
return {
publicVariable :'something else',
publicMethod2 : function(){
alert(Construct().property1);
Construct().method1();
}
};
})();
var t1 = mySingleton;
var t2 = mySingleton;
alert(t1===t2);//true
t1.publicMethod2();//hello world
t2.publicMethod2();//hello world
使用閉包的方式實現單例模式,可以實現封裝,相對安全。
閉包實現單例模式的方法多種多樣,這只是其中一種方式。
實例:
下面以登錄浮窗爲例,我們的目的是點擊登錄,創造唯一的浮窗。
利用單例模式的代碼如下:
var createLoginLayer = (function(){
var div;
return function(){
if(!div){
div = document.createElement("div");
div.innerHTML=("我是登錄浮窗");
div.style.display='none';
document.body.appendChild(div);
}
return div;
}
})();
document.getElementById( 'loginBtn' ).onclick = function(){
var loginLayer = createLoginLayer();
loginLayer.style.display = 'block';
};
這段代碼用單例模式實現了唯一浮窗,但是這段代碼仍然是違反單一職責原則的,創建對象和管理單例的邏輯都放在createLoginLayer對象內部。如果要創造唯一的script或其他元素不具備複用性,下面來對代碼進行優化,將不變的部分抽象出來。
代碼如下:
var getSingle = function( fn ){
var result;
return function(){
return result || ( result = fn .apply(this, arguments ) );
}
};
var createLoginLayer = function(){
var div = document.createElement( 'div' );
div.innerHTML = '我是登錄浮窗';
div.style.display = 'none';
document.body.appendChild( div );
return div;
};
var createSingleLoginLayer = getSingle( createLoginLayer );
document.getElementById( 'loginBtn' ).onclick = function(){
var loginLayer = createSingleLoginLayer();
loginLayer.style.display = 'block';
};
這時,如果要創建其他的,也可以很方便的創建。
var createSingleIframe = getSingle( function(){
var script = document.createElement ( 'script' );
document.body.appendChild( script );
return script;
});
單例模式優缺點:
優點:
1、提供了對唯一實例的受控訪問。
2、由於在系統內存中只存在一個對象,因此可以節約系統資源,對於一些需要頻繁創建和銷燬的對象單例模式無疑可以提高系統的性能。
3、允許可變數目的實例
缺點:
1、由於單利模式中沒有抽象層,因此單例類的擴展有很大的困難。
2、單例類的職責過重,在一定程度上違背了“單一職責原則”,可以優化。
總結:
這裏主要了解了最簡單的設計模式—單例模式,由於js的靈活性使得實現單例模式的方法多種多樣,單例模式的核心是確保只有一個實例,並提供全局訪問。使用數據緩存來存儲該單例,用作判斷單例是否已經生成,是單例模式主要的實現思路。