实现单例模式
/** 最简单的单例实现* 缺点:Singleton类的使用者必须知道这是一个单例类,跟以往通过new XXX的方式来获取对象不同,这里偏要使用Singleton.getInstance来获取对象* 所以此种方式创建单例的意义并不大*/var Singleton = function(name) {this.name = name;this.instance = null;};Singleton.prototype.getName = function() {alert(this.name);};Singleton.getInstance = function(name) {if (!this.instance) {this.instance = new Singleton(name);}return this.instance;};var a = Singleton.getInstance('sven1');var b = Singleton.getInstance('sven2');alert(a === b); // true
透明的单例模式
现在的目标是实现一个“透明”的单例类,用户从这个类中创建对象的时候,可以像使用其他任何普通类一样
var CreateDiv = ((function () {var instance = null;var CreateDiv = function (html) {if (instance) return instance;this.html = html;this.init();return instance = this;};return CreateDiv;})())CreateDiv.prototype.init = function () {var div = document.createElement('div');div.innerHTML = this.html;document.body.appendChild(div);}alert(a === b); // true
CreateDiv的构造函数实际上负责了两件事情。第一是创建对象和执行初始化init方法,第二是保证只有一个对象。虽然我们目前还没有接触过“单一职责原则”的概念,但可以明确的是,这是一种不好的做法,至少这个构造函数看起来很奇怪。
假设我们某天需要利用这个类,在页面中创建千千万万的div,即要让这个类从单例类变成一个普通的可产生多个实例的类,那我们必须得改写CreateDiv构造函数,把控制创建唯一对象的那一段去掉,这种修改会给我们带来不必要的烦恼。
用代理实现单例模式
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 = null;return function (html) {return instance || (instance = new CreateDiv(html));}})();var a = new ProxySingletonCreateDiv('AAA');var b = new ProxySingletonCreateDiv('BBB');alert(a === b); // true
JavaScript中的单例模式
作为普通的开发者,我们有必要尽量减少全局变量的使用,即使需要,也要把它的污染降到最低。以下几种方式可以相对降低全局变量带来的命名污染.
- 使用命名空间
var namespace1 = {a: function () {alert(1);},b: function () {alert(2);},}
- 使用闭包封装私有变量
var user = (function () {var __name = 'sven',__age = 12;return {getUserInfo: function () {return __name + '-' + __age;}}})();
惰性单例
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对象内部。
- 如果我们下次需要创建页面中唯一的iframe,或者script标签,用来跨域请求数据,就必须得如法炮制,把createLoginLayer函数几乎照抄一遍
通用的惰性单例
var getSingle = function (fn) {var result = null;return function () {return result || (result = fn.apply(this, arguments));}}var createLoginLayer = function (...args) {console.warn(args); // [1, 2, 3]var div = document.createElement('div');div.innerHTML = '我是登录浮窗';div.style.display = 'none';document.body.appendChild(div);return div;};var singletonCreateLoginLayer = getSingle(createLoginLayer);document.querySelector('#demo').addEventListener('click', function () {var layer = singletonCreateLoginLayer(1,2,3);layer.style.display = 'block';});
