用一个变量来标志当前是否已经为某个类创建过对象,如果是,则在下一次获取该类的实例时,直接返回之前创建的对象。
实现单例模式
//1.实现单例模式
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’);
console.log(a === b); //true
以上,我们通过Singleton.getInstance
来获取Singleton
类的唯一对象,虽然简单但是增加了这个类的“不透明性”,Singleton
类的使用者必须知道这是一个单例类,跟以往通过new xxx
的方法不同
透明的单例模式
有了以上的不足,我们现在就来解决
//透明的单例模式
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 aa = new CreateDiv('sven1');
var bb = new CreateDiv('sven2');
console.log(aa === bb);
以上代码,CreateDiv做了创建对象和执行初始化init方法,且保证只有一个对象。
但是,假如有一天我们需要利用这个类,在页面中创建千千万万个div,即要让这个类从单例类变成一个普通的可以产生多个实例的类,那我们必须改写CreateDiv构造函数,把控制创建唯一的那一段去掉…这样好像有点太麻烦了
用代理实现单例模式
这里就可以引入代理类的方式,思想就是将负责管理的单例逻辑移到一个代理类中,这样CreateDiv就变成一个普通的类,代码此处省略…
JavaScript中的单例模式
前面几种单例模式的实现,更多是接近传统面向对象语言中的实现,单例从“类”中创建出来。
然而,js作为一个没有类的语言,生搬单例模式的概念并无意义。我们只要一个对象,为什么要创建一个类呢?我们只是要一个唯一的实例就好了…因此我们可以直接用var a = {}
来创建对象字面量。
惰性单例
惰性单例:需要的时候才创建。
例如,以一个登录悬浮窗为例,我们可以在页面加载的时候创建好这个div窗口,然后点击登录按钮的时候开始显示,但是这样如果用户只是随便浏览下就白白浪费了一些dom节点。
还有一种方法,点击的时候再创建,关闭的时候销毁。但是如果用户频繁的进行注销等操作,我们就要频繁的创建和删除div,显然这也不合理。
所以,这时候惰性单例模式就出马了。
var createLoginLayer = (function() {
var div;
return function() {
if (!div) {
div = document.createElement(‘div’);
//创建添加等dom操作…
}
return div;
}
})();
$("xxx").onclick = function() {
var loginLayer = createLoginLayer();
loginLayer.show();
}
通用的惰性实例
看起来好像没什么问题,然而,他还是违反了单一的原则,创建的对象逻辑都放在createLoginLayer内部,如果我们要创建别的东西例如iframe,就要几乎将这个函数照抄一遍了,因此我们应该把不变的部分隔离出来。
var getSingle = function(fn) {
var result;
return function() {
return result || (result = fn.apply(this, arguments));
}
};
var createLoginLayer = function() {
//创建登录框
}
var createSingleLoginLayer = getSingle(createLoginLayer);
于是,目的达成。
当然,单例模式用途远远不止于创建对象,比如我们在渲染完页面的时候,接下来要给这个列表绑定click事件,如果是ajax动态向列表里面加载数据,实际上只需要第一次渲染的时候绑定。如果不用jq的$("").one('click', function(){})
的话,我们也可以用单例模式,做法类似。