文章目錄
- 1、實現單例模式
- 2、透明的單例模式
- 3、用代理實現單例模式
- 4、JavaScript 中的單例模式
- 5、惰性單例
- 6、通用的惰性單例
- 7、小結
定義: 保證一個類僅有一個實例,并提供一個訪問它的全局訪問點
單例模式是一種常用的模式,有一些對象我們往往只需要一個,比如線程池、全局緩存、瀏
覽器中的 window 對象等
1、實現單例模式
是用一個變量來標志當前是否已經為某個類創建過對象,如果是,則在下一次獲取該類的實例時,直接返回之前創建的對象。
代碼如下:
var Singleton = function (name) {this.name = name;this.instance = null;
};
Singleton.getInstance = function (name) {if (!this.instance) {this.instance = new Singleton(name);}return this.instance;
};var a = Singleton.getInstance('a');
var b = Singleton.getInstance('b');
console.log(a === b); // true
或者利用閉包:
var Singleton = function (name) {this.name = name;this.instance = null;
};
Singleton.getInstance = (function (name) {var instance = null;return function (name) {if (!instance) {instance = new Singleton(name);}return instance;};
})();var a = Singleton.getInstance('a');
var b = Singleton.getInstance('b');
console.log(a === b); // true
問題:
就是增加了這個類的“不透明性”,Singleton 類的使用者必須知道這是一個單例類
2、透明的單例模式
實現一個“透明”的單例類,用戶從這個類中創建對象的時候,可以像使用其他任何普通類一樣。
在下面的例子中,我們將使用 CreateDiv 單例類,它的作用是負責在頁面中創建唯一的 div 節點,代碼如下:
var CreateDiv = (function () {var instance = null;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('a');
var b = new CreateDiv('b');console.log(a === b);
問題:
問題:假設我們某天需要利用這個類,在頁面中創建千千萬萬的 div,即要讓這個類從單例類變成一個普通的可產生多個實例的類,那我們必須得改寫 CreateDiv 構造函數,把控制創建唯一對象的那一段去掉,這種修改會給我們帶來不必要的煩惱
解決:
使用代理模式
3、用代理實現單例模式
在 CreateDiv 構造函數中,把負責管理單例的代碼移除出去,使它成為一個普通的創建 div 的類:
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);
};// 引入代理類 proxySingletonCreateDiv:
var ProxySingletonCreateDiv = (function () {var instance;return function (html) {if (!instance) {instance = new CreateDiv(html);}return instance;};
})();var a = new ProxySingletonCreateDiv('a');
var b = new ProxySingletonCreateDiv('b');console.log(a === b);
跟之前不同的是,現在我們把負責管理單例的邏輯移到了代理類 proxySingletonCreateDiv 中。這樣一來,CreateDiv 就變成了一個普通的類,它跟 proxySingletonCreateDiv 組合起來可以達到單例模式的效果
4、JavaScript 中的單例模式
在 JavaScript 開發中,我們經常會把全局變量當成單例來使用。
例如:
var a = {};
問題:
全局變量存在很多問題,它很容易造成命名空間污染
解決:
1、使用命名空間
var namespace1 = { a: function(){ alert (1); }, b: function(){ alert (2); }
};
2、使用閉包封裝私有變量
var user = (function(){ var __name = 'sven', __age = 29; return { getUserInfo: function(){ return __name + '-' + __age; } }
})();
5、惰性單例
惰性單例指的是在需要的時候才創建對象實例
例子:
var createLoginLayer = (function () {var div;return function () {if (!div) {div = document.createElement('div');div.innerHTML = '我是浮窗';document.body.appendChild(div);}return div;}
})()// 用戶點擊按鈕的時候才開始創建該浮窗
document.getElementById('loginBtn').onclick = function () {var loginLayer = createLoginLayer();
};
問題:違反單一職責原則,創建對象和管理單例的邏輯都放在 createLoginLayer對象內部,如果我們下次需要創建頁面中唯一的 iframe,或者 script 標簽,用來跨域請求數據,就
必須得如法炮制,把 createLoginLayer 函數幾乎照抄一遍
解決:第6點
6、通用的惰性單例
將單例的邏輯從原來的代碼中抽離出來,這些邏輯被封裝在 getSingle函數內部,創建對象的方法 fn 被當成參數動態傳入 getSingle 函數
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 = '我是浮窗';document.body.appendChild(div);return div;
}
var createSingleLoginLayer = getSingle(createLoginLayer);document.getElementById('loginBtn').onclick = function () {var loginLayer = createSingleLoginLayer();
};
也可以用在只執行一次的函數
比如:給一個div綁定事件
var bindEvent = getSingle(function () {console.log('給div綁定事件~');return true;
});bindEvent();
bindEvent();
bindEvent();
7、小結
單例模式是一種簡單但非常實用的模式,特別是惰性單例技術,在合適的時候才創建對象,并且只創建唯一的一個。更奇妙的是,創建對象和管理單例的職責被分布在兩個不同的方法中,這兩個方法組合起來才具有單例模式的威力。