JavaScript設(shè)計模式二(單例模式)
這邊文章主要是JavaScript中的單例模式
定義:
保證一個類僅有一個實例,并提供一個訪問它的全局訪問點
其實我們的日常開發(fā)中或多或少的用到了單例模式的方法穷缤。例如我們做Electron開發(fā)的過程中,點擊一個按鈕創(chuàng)建了一個窗口感论,后續(xù)點擊的時候绅项,如果窗口已經(jīng)存在了就focus窗口,否則創(chuàng)建比肄;或者我們經(jīng)常會創(chuàng)建一個定時任務(wù)快耿,同時把定時任務(wù)賦值給一個變量,如果變量不存在就創(chuàng)建芳绩,否則不創(chuàng)建掀亥。但是大多數(shù)情況我們都是利用的是一個變量來控制的。接下來我們看看代碼的實現(xiàn)
實現(xiàn)單例模式
其實上面的介紹我們已經(jīng)說了實現(xiàn)單例模式的思路妥色,就是通過一個變量來控制
var Singleton=function(name) {
this.name=name;
this.instance=null;
}
Singleton.prototype.getName=function(){
console.log(this.name);
}
Singleton.getInstance=function(name) {
if(!this.instance) {
this.instance = new Singleton(name);
}
return this.instance;
}
//這種寫法借助了this.instance搪花,其實可以不需要
var Singleton = function(name) {
this.name=name;
}
Singleton.prototype.getName=function(){
console.log(this.name);
}
Singleton.getInstance=(function(){
var instance = null;
return function(name) {
if(!instance) {
instance = new Singleton(name);
}
return instance;
};
})();
使用方法:
var a = Singleton.getInstance('a');
var b = Singleton.getInstance('b');
a===b
這種方式實現(xiàn)了我們說的單例模式,但是有一個很明顯的缺點嘹害,就是我們實例化的時候不是使用的new方法來實例化撮竿,而是用的getInstance方法,也就是說我們必須知道這個類是單例類笔呀,才能這樣去用幢踏,這就增加了不透明性。
透明的單例模式
所謂的透明的單例模式许师,就是我們可以像正常的類那樣去new一個單例類房蝉。
var CreateDiv = (function(){
var instance;
var CreateDiv = function(html) {
if (instance) {
return instance;
}
this.html = html;
this.init();
return instance = this;
};
CreateDiv.prototype.init = function() {
var div = document.createElement('div');
div.innerHTML = this.html;
document.body.appendChild('div');
};
return CreateDiv;
})();
var a = new CreateDiv('div');
var b = new CreateDiv('div');
a===b
之所以能夠通過new來創(chuàng)建一個單例類的實例僚匆,是因為CreateDiv
的返回值是一個構(gòu)造函數(shù),這個構(gòu)造函數(shù)做了兩件事情
- 創(chuàng)建對象和執(zhí)行init方法
- 保證只有一個對象
我們可以想象如果需求變成了搭幻,我們需要CreateInput之類咧擂,是不是一直要修改這個類呢?
代理實現(xiàn)單例模式
利用代理就可以很好的解決上面的問題
var CreateDiv = function(html) {
this.html = html;
this.init();
}
CreateDiv.prototype.init = function() {
var div = document.createElement('div');
div.innerHTML = this.html;
document.body.appendChild('div');
};
var ProxySingletonCreateDiv = (function(){
var instance;
return function(html) {
if (!instance) {
instance = new CreateDiv(html);
}
return instance;
}
})();
var a = new ProxySingletonCreateDiv('div');
var b = new ProxySingletonCreateDiv('div');
利用代理類檀蹋,我們遵循了單一職責(zé)的原則松申,讓代理類負責(zé)單例的邏輯,CreateDiv變成一個普通的創(chuàng)建html的類续扔,兩者結(jié)合達到單例模式的效果
JavaScript中的單例模式
JavaScript單例模式的核心是:
確保只有一個實例攻臀,并提供全局訪問
與傳統(tǒng)的面向?qū)ο笳Z言不一樣焕数,JavaScript可以定義全局變量,而且我們通常認(rèn)為全局變量就是一個單例识脆,但是這種使用方式很容易造成命名空間的污染善已,針對這種問題有兩種辦法
- 使用命名空間
最簡單的就是使用字面量常量:
var namespace1 = {
a: function(){},
b: 'bbbb'
}
- 利用閉包封裝變量
var user = (function(){
var _user = 'hahaha';
return {
setUserName: function(name) {
_user = name;
},
getUserName: function(){
console.log(_user);
}
}
})();
惰性單例
定義:
惰性單例是指需要時才創(chuàng)建的單例
上面的那幾種方法實際上就是惰性單例,但是那些事面向?qū)ο蟮南こ恚覀兛纯碕avaScript中的惰性單例艘包,看一段PC版的代碼吧
let historyWindow = null;
ipc.on(cfg.CHANNEL.LOCAL.CHAT.SEARCH_HISTORY, function(event, arg) {
if (!historyWindow) {
historyWindow = new BrowserWindow({
width: 621,
height: 540,
minWidth: 621,
minHeight: 540,
frame: false,
show: false,
});
historyWindow.setAutoHideMenuBar(false);
historyWindow.loadURL('file://' + __dirname + '/../../../client/views/history.html');
if (process.env.NODE_ENV == 'dev') {
historyWindow.webContents.openDevTools();
}
historyWindow.on('close', function() {
historyWindow.webContents.closeDevTools();
});
historyWindow.on('closed', function() {
glb.set(cfg.GLB.HISTORY_WINDOW, null);
glb.remove(cfg.GLB.ADD_MEMBER_WINDOW);
historyWindow = null;
});
historyWindow.webContents.on('did-finish-load', function() {
glb.set(cfg.GLB.HISTORY_WINDOW, historyWindow);
historyWindow.show();
historyWindow.focus();
historyWindow.webContents.send(cfg.CHANNEL.LOCAL.CHAT.OPEN_HISTORY_WINDOW_RECV, arg);
});
} else {
historyWindow.webContents.send(cfg.CHANNEL.LOCAL.CHAT.OPEN_HISTORY_WINDOW_RECV, arg);
if (historyWindow.isMinimized()) {
historyWindow.show();
} else {
historyWindow.focus();
}
}
});
這段代碼的邏輯是想虎,點擊歷史消息的icon,創(chuàng)建一個歷史消息的彈出框岂却,后面如果繼續(xù)點擊裙椭,就把之前的彈出框focus。
這里其實還有優(yōu)化的空間扫尺,我們知道electron創(chuàng)建一個新的BrowserWindow是很慢的,所以我們創(chuàng)建一次之后你雌,用戶點擊關(guān)閉二汛,其實可以隱藏起來肴颊,并不是實際的關(guān)閉,這樣當(dāng)用戶點擊第二次的時候就省略了創(chuàng)建窗口的過程婿着,直接渲染數(shù)據(jù)就可以醋界。