定义
保证一个类仅有一个实例,并提供一个访问它的全局反问点
场景
线程池,全局缓存,浏览器中的window对象等
实现单例模式(简单的例子)
var Singleton = function (name) {
this.name = name;
};
Singleton.instance = null;
Singleton.prototype.getName = function () {
console.log(this.name);
};
Singleton.getInstance = function name(name) {
if (!this.instance) {
this.instance = new Singleton(name);
}
return this.instance;
};
var a = Singleton.getInstance("sven1");
var b = Singleton.getInstance("sven2");
console.log(a === b);
我们通过Singleton.getInstance,来获取Singleton类的唯一对象,这种方式相对简单,但有一个问题,就是增加了这个类的“不透明性”,Singleton类的使用者必须知道这是一个单例类,跟通过new xxx的方式来获取对象不同,这里偏要使用Singleton.getInstance来获取对象
透明的单例模式
实现一个“透明”的单例类,用户从这个类中创建对象的时候,可以像使用其他任何普通类一样。
使用`CreateDiv单例类,它的作用是负责在页面中创建唯一的div子节点
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 div1 = new CreateDiv("div1");
var div2 = new CreateDiv("div2");
console.log(div1 === div2);
虽然完成一个透明的单例模式,但它的缺点也是非常明显,使用了自执行的匿名函数和闭包,并且让这个匿名函数返回真正的CreateDiv的构造方法,这增加了一些程序的复杂度,阅读起来也不是很舒服
把上面列子代码进行解耦
把上面例子的代码进行解耦,让它更加容易阅读,减轻复杂度,首先在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;
return function (html) {
if (!instance) instance = new CreateDiv(html);
return instance;
};
})();
JavaScript 中的单例模式
前面提到的几种单例模式的实现,更多的是接近传统面向对象语言中的实现,单例对象从类中创建而来。在以类为中心的语言中,这是很自然的做法。
但JavaScript其实是一门无类语言,也正因为如此,生搬单例模式的概念并无意义。在js中创建对象的方法非常简单,既然我们只需要一个“唯一”的对象,为什么要为它创建一个类呢?
单例模式的核心是确保只有一个实例,并提供全局访问
全局变量不是单例模式,但在js开发中,我们经常会把全局变量当成单例模式来使用
列如
var a = {}
当用这种方式创建对象a时,对象a确实是独一无二的。如果a变量被声明在全局作用域下,则我们可以在代码中的任意位置使用这个变量,全局变量提供给全局访问是理所当然的。这样就满足单例模式的两个条件
但全局变量存在很多问题,它很容易造成命名空间污染。就像上面的对象a一样,随时可能被别人覆盖。开发中有必要减少全局变量的使用,即时需要,也要把它的污染降到最低。