单例模式的两个要求:保证一个类仅有一个实例,并提供一个访问它的全局访问点
function Singleton(name){
this.name = name
}
Singleton.prototype.showName = function(){
console.log(this.name)
}
var instance = null
Singleton.getInstance = function(name){
if (!instance) {
instance = new Singleton(name)
}
return instance
}
上述方式中声明的变量instance会污染全局命名空间,可以改为下面这种写法
function Singleton(name){
this.name = name
}
Singleton.prototype.showName = function(){
console.log(this.name)
}
Singleton.getInstance = (function(){
var instance;
return function(name){
if (!instance) {
instance = new Singleton(name)
}
return instance
}
})()
采用闭包的方式将变量instance封装在匿名函数中,避免了对全局环境的污染。但是这种单例模式有一个弊端,使用者必须知道这是一个单例类,必须使用Singleton.getInstance
来获取对象。我们更喜欢的方式仍然是使用new关键字来创建对象。继续修改代码,使之对使用者透明。
var Singleton = function(){
var instance
var SingletonInner = function (name) {
if(!instance){
this.name = name
return instance = this
} else {
return instance
}
}
SingletonInner.prototype.showName = function(){
console.log(this.name)
}
return SingletonInner
}()
var a = new Singleton("s1")
var b = new Singleton("s2")
a === b // true
以上的修改中,Singleton构造函数承担了两项职责,创建对象和保证单例。鉴于职责单一的编码原则,最好将其拆分开来。
var Singleton = function(name){
this.name = name
}
Singleton.prototype.showName = function(){
console.log(this.name)
}
var proxy = function(){
var instance ;
return function(name){
if(!instance){
instance = new Singleton(name)
}
return instance
}
}()
可以看到,修改后的写法与最开始的写法相似,只不过把Singleton.getInstance换成了proxy。
在javascript中,类的概念相对弱化,更多的是执行脚本语句,即执行一个一个的函数。那么如何确保一段代码只执行一次呢?单例的用途也可以适用在普通函数上。看下面代码
var f = function(){
var a= 1;
console.log(a)
return a
}
var getSingle = function(fn){
var instance = null;
return function(){
return instance || (instance = fn.apply(this,arguments))
}
}
var singleFn = getSingle(f)
在实际使用中,上面这种用法可能更为常见,将偏重业务逻辑的函数和保证单例功能的函数拆分开来,使程序更加灵活。