定义
单例模式,保证一个类仅有一个实例,并提供一个访问它的全局访问点。
自己的理解
通常我们可以让一个全局变量使得一个对象被访问,但它不能防止实例化多个对象。一个最好的办法就是,让类自身负责保持它的唯一实例,且这个类保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。
实现单例模式
要实现一个简单的单例模式并不复杂,无非是用一个变量来缓存一个类实例化生成的对象,然后用这个变量来判断一个类是否已经实例化过。如何变量有值,则在下一次要获取该类的实例化生成的对象时,直接返回这个变量(之前实例化生成的对象)。
var Singleton = function(name) {
this.name = name;
this.instance = null;
};
Singleton.prototype.init = function() {
//...初始化
};
Singleton.getInstance = function(name) {
if (!this.instance) {
this.instance = new Singleton(name);
this.instance.init();
}
return this.instance;
};
var a = Singleton.getInstance('a');
var b = Singleton.getInstance('b');
console.log(a === b); // true
正常来说一个类每次实例化生成的对象是不同的,在上述代码中分别使用Singleton.getInstance
获取Singleton
类实例化生成的对象并赋值给对象a和对象b,执行console.log(a === b)
打印出true
,说明上述代码实现单例模式成功。
或不把实例化生成的对象挂载到类的属性上也可以实现,代码如下所示:
var Sington = function( name ){
this.name = name;
};
Singleton.prototype.init = function() {
//...初始化
};
Singleton.getInstance = (function(){
var instance = null;
return function( name ){
if ( !instance ){
instance = new Singleton( name );
instance.init();
}
return instance;
}
})();
变量instance
用来缓存类实例化生成的对象,用自执行匿名函数创建了一个闭包,把变量instance
存储在闭包中,每次执行Singleton.getInstance
时就可以用变量instance
来判断Singleton
类是否已经实例化过。
透明的单例模式
上述实现单例模式的方法中有一个问题,就是增加了这个类的“不透明性”,Singleton
类的使用者必须知道这是一个单例类,跟以往通过new XXX
的方式来实例化类,这里要使用Singleton.getInstance
来实例类。故要换一种方法来实现透明的单例模式。
const Singleton = (function() {
let instance;
const Singleton = function(name) {
if (instance) {
return instance;
}
this.name = name;
this.init();
return instance = this;
};
Singleton.prototype.init = function() {
//...初始化
};
return Singleton;
})();
const a = new Singleton('a');
const b = new Singleton('b');
console.log(a === b); // true
以上代码中,Singleton
类的构造函数是一个自执行的匿名函数,会构成一个闭包,可以把判断类是否实例化过的和缓存类实例化生成的对象的变量instance
缓存起来,最后在返回真正的Singleton
类构造方法。那么就可以使用new Singleton
来实例化Singleton
类。
必须遵循设计模式的原则
上述代码还不够完美,有两点:
-
把
Singleton
类的真正的构造函数放在自执行的匿名函数中返回,有点奇怪。 -
Singleton
类的构造函数中违背了设计模式的单一职责原则,做了二件事情,创建类实例化生成的对象和执行初始化init
方法,保证了只能实例化一次。
实现任何设计模式都要遵循设计模式的原则,要把“创建类实例化生成的对象和执行初始化init
方法”和“保证了只能实例化一次”这两个职责分隔开来。下面用代理实现单例模式。
const Singleton = function (name) {
this.name = name;
this.init();
}
Singleton.prototype.init = function () {
//...初始化
};
const PropxSingleton = (function () {
var instance;
return function (name) {
if (!instance) {
instance = new Singleton(name);
}
return instance;
}
})();
var a = new PropxSingleton('a');
var b = new PropxSingleton('b');
console.log(a === b);//true
以上用代理类propxSingleton
实现了Singleton
类的单例模式,而Singleton
类仍旧是一个普通的类,不影响在其它地方的使用。
然后以上代码还不够完美,假如要给另外一个类实现单例模式,得去修改代理类propxSingleton
的代码,不符合设计模式中的开放-封闭原则,得封装一个获取不同类的代理类propxSingleton
的getPropxSingleton
方法。
const Singleton = function (name) {
this.name = name;
this.init();
}
Singleton.prototype.init = function () {
//...初始化
};
const getPropxSingleton = function (customClass) {
return (function () {
var instance;
return function (name) {
if (!instance) {
instance = new customClass(name);
}
return instance;
}
})()
};
const PropxSingleton = getPropxSingleton(Singleton);
var a = new PropxSingleton('a');
var b = new PropxSingleton('b');
console.log(a === b); //true
用ES6来实现一个单例模式
以上都是用ES5实现的单例模式,现在都是使用ES6了,故用ES6来实现一个单例模式。
class Singleton {
constructor(name) {
this.name = name;
this.init();
}
init() {
//...初始化
}
}
const getPropxSingleton = function (customClass) {
let instance = null;
return class {
constructor() {
if (instance) return instance;
return instance = new customClass(...arguments)
}
}
};
const PropxSingleton = getPropxSingleton(Singleton);
var a = new PropxSingleton('a');
var b = new PropxSingleton('b');
console.log(a === b); //true