單例模式:限制類實例化次數只能一次,一個類只有一個實例,並提供全局訪問點。(歸屬創建型設計模式)
模式特點
- 類只有一個實例
- 全局可訪問該實例
- 自行實例化(主動實例化)
- 可推遲初始化,即延遲執行(與靜態類/對象的區別)
實現方式
實現方式:使用一個變量存儲類實例對象(值初始爲 null/undefined
)。進行類實例化時,判斷類實例對象是否存在,存在則返回該實例,不存在則創建類實例後返回。多次調用類生成實例方法,返回的同一個實例對象。
代碼實現
class Singleton {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
Singleton.getInstance = (function () {
let instance;
return function (name) {
if (!instance) {
instance = new Singleton(name);
}
return instance;
}
})();
var Winner = Singleton.getInstance('Winner');
var Looser = Singleton.getInstance('Looser');
console.log(Winner === Looser); // true
console.log(Winner.getName()); // 'Wiinner'
console.log(Looser.getName()); // 'Looser'
代碼中定義了一個 Singleton
類,並定義 Singleton.getInstance()
方法來生成實例對象。而不是通過 new
操作符來創建類實例。
在 Singleton.getInstance()
中,通過閉包引用存儲類實例的對象,判斷類是否實例化過。(這裏閉包的使用能夠使 getInstance()
多次調用都能獲取到之前類實例的存儲值)。因此無論執行多少次 Singleton.getInstance()
方法,都只會返回同一個Singleton類實例對象。
使用場景
“單例模式的特點,意圖解決:維護一個全局實例對象。”
- 引用第三方庫
- 彈窗登錄框
- 購物車
- 全局態管理 store (Vuex / Redux)
項目中引入第三方庫時,重複加載庫文件,全局只會實例化一個庫對象,如 jQuery
,lodash
,moment
..., 其實它們的實現方式也是單例模式應用的一種:
// 引入代碼庫 libs(庫別名)
if (window.libs != null) {
return window.libs; // 直接返回
} else {
window.libs = '...'; // 初始化
}
單例設計模式實現全局彈窗登錄框:
class LoginModal {
constructor() {
const div = document.createElement('div');
div.innerHTML = '登錄框HTML內容';
div.style.display = 'none';
this.el = div;
this.visible = false;
document.body.append(div);
return div;
}
show() {
this.el.style.display = 'block';
}
hide() {
this.el.style.display = 'none';
}
toggle() {
this.visible = !this.visible;
this.visible ? this.show() : this.hide();
}
}
LoginModal.getInstance = (function() {
let instance;
return function() {
if (!instance) {
instance = new LoginModal();
}
return instance;
}
})();
// 按鈕切換顯示登錄框
document.getElementById('btn-login').addEventListener('click', function(){
LoginModal.getInstance().toggle();
})
優缺點
- 優點:適用單一對象,只生成一個對象實例,減少內存開銷,避免頻繁創建和銷燬實例。
- 缺點:不適用動態擴展對象,如通過對象繼承擴展新對象。
TIPS: 多線程編程語言中,單例模式會涉及同步鎖的問題。而JavaScript是單線程的編程語言,可忽略該問題。
參考文章
本文首發Github,期待Star!
https://github.com/ZengLingYong/blog
作者:以樂之名
本文原創,有不當的地方歡迎指出。轉載請指明出處。