设计模式 - 单例模式
传统单例实现
基础实现
// 方式一:instance维护在对象中
// ES5
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
// ES6 && TS
class Demo {
private static instance: Demo // 仅当前类内部&仅类内部 访问
static getInstance() { // 仅类内部 访问
if(!this.instance) {
this.instance = new Demo()
}
return this.instance
}
}
// 缺点:
// 1. 构造函数中,使用了与该构造函数无关的变量标识 instance
// 2. 增加了这个类的“不透明性”。(使用者必须知道这是一个单例类,跟以往通过 new XXX 的方式来获取对象不同,这里要使用 Singleton.getInstance 来获取对象)
// 方式二:instance维护在闭包中
var Singleton = function (name) {
this.name = name;
};
Singleton.prototype.getName = function () {
alert(this.name);
};
Singleton.getInstance = (function () {
var instance = null
return function (name) {
if (!instance) {
instance = new Singleton(name)
}
return instance
}
})()
var a = Singleton.getInstance('sven1')
var b = Singleton.getInstance('sven2')
alert(a === b) // true
// 缺点:
// 1. 增加了这个类的“不透明性”。
透明的单例模式
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('sven1');
var b = new CreateDiv('sven2');
alert(a === b); // true
// 缺点:
// 1. 违反“单一职责原则”(构造函数做了2件事:1.创建对象和执行初始化 init 方法 2.保证只有一个对象)
// 2. 不好维护。如果单例类变成一个普通的可产生多个实例的类,必须得改写 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);
};
// 来引入代理类 proxySingletonCreateDiv
var ProxySingletonCreateDiv = (function () {
var instance;
return function (html) {
if (!instance) {
instance = new CreateDiv(html);
}
return instance;
}
})();
var a = new ProxySingletonCreateDiv('sven1');
var b = new ProxySingletonCreateDiv('sven2');
alert(a === b);
// 优点
// 1.符合“单一职责原则” (负责管理单例的逻辑移到了代理类 proxySingletonCreateDiv 中。这样一来,CreateDiv 就变成了一个普通的类)
针对JavaScript常用的单例模式
JavaScript 其实是一门无类(class-free)语言,也正因为如此,生搬单例模式的概念并无
意义。在 JavaScript 中创建对象的方法非常简单,既然我们只需要一个“唯一”的对象,为什
么要为它先创建一个“类”呢?这无异于穿棉衣洗澡,传统的单例模式实现在 JavaScript 中并
不适用。
惰性单例
惰性单例指的是在需要的时候才创建对象实例。而不是在页面加载好
的时候就创建。这样如果交互场景下,没有被使用到,更节省资源。
// 惰性单例 - 创建过登录浮窗
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';
};
通用的惰性单例
将创建单例和功能函数拆开
// 通用惰性单例函数
var getSingle = function (fn) {
var result;
return function () {
return result || (result = fn.apply(this, arguments));
}
};
// 使用1 创建登录弹窗
var createLoginLayer = function () {
var div = document.createElement('div');
div.innerHTML = '我是登录浮窗';
div.style.display = 'none';
document.body.appendChild(div);
return div;
};
var createSingleLoginLayer = getSingle(createLoginLayer);
document.getElementById('loginBtn').onclick = function () {
var loginLayer = createSingleLoginLayer();
loginLayer.style.display = 'block';
};
// 使用2 创建 iframe
var createIframe = function () {
var iframe = document.createElement('iframe');
document.body.appendChild(iframe);
return iframe;
};
var createSingleIframe = getSingle(createIframe);
document.getElementById('loginBtn').onclick = function () {
var loginLayer = createSingleIframe();
loginLayer.src = 'http://baidu.com';
};
参考资料
[1] 曾探. JavaScript设计模式与开发实践. 北京:人民邮电出版社,2015