在軟件開發過程中,我們往往對於一些資源,只需要全局性的一個。比如說唯一的一個線程池,瀏覽器窗口中唯一的一個window對象等等。這時候,我們需要一類只能有一個實例,並提供一個訪問它的全局訪問點,這就是單例模式的概念。
實現單例模式最簡單的,就是設置一個標誌,又這個標誌來判斷是否已經創建了該類。
var singleton = function( name ){
this.name = name;
this.instance = null;
}
singleton.prototype.sayName = function(){
console.log(this.name);
}
singleton.getInstance = function(name){
if(!this.instance)
{
this.instance = new singleton(name);
}
return this.instance;
}
//測試
var ins1 = singleton.getInstance('liu');
var ins2 = singleton.getInstance('yang');
ins1 == ins2 //返回爲true;並且此時調ins2.sayName打印的是'liu'
這就是一個單例,但是這種實現方式是有缺點的,必須規定創建類用singleton.getInstance方法纔可以,否則這個類我們照樣也可以用new來創建,這時候這就不是一個單例的模式了。
爲了可以用new
關鍵字來創建單例,跟普通實習的創建方式相同,我們可以改寫成下面這樣:
var singleton = (function( name ){
var instance;
var create = function(name){
if(instance)
{
return instance;
}
this.name = name;
return instance=this;
}
return create;
})();
var a = new singleton('liu');
var b = new singleton('yang');
a == b; //返回true
實際創建的是create這個類,並且只會實例一次。
最後來寫一個通用的單例,比如我們有些時候想把一個對象作爲普通對象去使用,有時候又想作爲單例去使用,而且我一個單例的函數就可以生成很多種不同類型的單例,這時候可以將實現單例時共有的部分抽出來,而將一些個性作爲參數傳到函數裏面。比如下面這種方式:
function Poll(){
this.name='線程池';
return this;
}
function Global(){
this.name = 'window對象';
return this;
}
var single = function(fn){
var instance;
return function(){
return instance || (instance=fn.apply(this, arguments))
}
}
//創建線程池單例的函數
var pollInstance = single(Poll);
//創建Window對象單例的函數
var winInstance = single(Global);
上面兩個函數其實是相同的函數,不同的是傳的參數不同,同時這裏利用了閉包,即每次運行pollInstance
之後的instance
變量是不會從內存中消失的。
單例模式有很多應用,比如我們給某個DOM
添加點擊事件,添加一次就可以了,這時候可以利用一個單例模式;或者我們做一個輪播圖,只需要整個div
中有一個img
元素,每次去換src
就可以,這時候就可以把創建img
元素加到div
中的操作用單例模式來實現等等。