实现单例模式
/** 最简单的单例实现
* 缺点: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';
});