定义
保证一个类仅有一个实例,并提供一个访问它的全局访问点。 单例模式应该是比较常见的一种设计模式了,比如浏览器中window对象。
实现单例模式
实现单例模式需要一个变量标志这个类是否已经创建过对象,如果是,就直接返回这个创建过的对象,如果不是用这个类新创建一个对象发挥出去。
var Singleton = function(name){
this.name = name
}
Singleton.instance =null
Singleton.prototype.getName = function(){
console.log(this.name)
}
Singleton.getInstance = function(){
if(!this.instance){
this.instance = new Singleton(name)
}
return this.instance
}
var a = Singleton.getInstance('name1')
var b = Singleton.getInstance('name2')
console.log(a===b) // true
这个例子有个问题,就是需要使用者知道Singleton是一个单例类,还必须使用Singleton.getInstance来获取对象,增加了不透明性。
透明的单例模式
下面的例子实现了一个“透明”的单例模式。
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.createElent('div')
div.innerHTML = this.html
document.body.appendChild(div)
}
return CreateDiv
})()
var a = new CreateDiv('name1')
var b = new CreateDiv('name2')
console.log(a===b) // true
虽然实现了“透明”的单例模式,但是有两个缺陷: 1.自执行的匿名函数和闭包,不利于阅读;2.CreateDiv这个方法其实做了两件事情,一是保证只有一个对象,而是创建对象,没有遵循“单一职责原则”,不是特别好的做法。假如我们并不需要只创建唯一对象,而是想创建很多对象,那就需要修改CreateDiv方法了。
用代理实现单例模式
现在用代理类的方式,解决上面的问题。CreateDiv就用来创建div,保证唯一对象用代理类实现。
var CreateDiv = function(html){
this.html = html
this.init()
}
CreateDiv.prototype.init = function(){
var div = document.createElent('div')
div.innerHTML = this.html
document.body.appendChild(div)
}
//引入代理类
var ProxySingletonCreateDiv = (function(){
var instance
return functoin(html){
if(!instance){
instance = new CreateDiv(html)
}
return instance
}
})()
var a = new ProxySingletonCreateDiv('name1')
var b = new ProxySingletonCreateDiv('name2')
console.log(a===b) // true
JavaScript中的单例模式
我们知道JS中没有类的概念,是ES6才出现类的概念,单例模式的核心是确保只有一个实例, 并提供全局访问。全局变量不是单例模式,但在JS开发中,会把全局变量单例模式来使用。
但是全局变量存在很多问题,造成命名空间的污染,一不小心就会命名冲突,所以在JS中有以下几种方式,降低命名空间污染的问题。
- 使用命名空间
- 使用闭包封装私有变量
但这不是最好的解决办法,后面就有了模块化的概念,只要不在一个模块中命名冲突即可,完美的解决了全局命名冲突的问题。
惰性单例
惰性单例是指在需要的时候再创建对象,这样可以提高一些性能,比如我们在调用 Singleton.getInstance再创建对象,而不是在页面加载好后就立即创建。
通用的惰性单例:
var getSingle = function( fn ){
var result;
return function(){
return result || ( result = fn .apply(this, arguments ) );
}
};
然后在登录场景中使用举例:
var createLoginLayer = function(){
var div = document.createElement( 'div' );
div.innerHTML = '我是登录浮窗';
div.style.display = 'none';
document.body.appendChild( div );
return div;
};
var createSingleLoginLayer = getSingle( createLoginLayer );
document.getElementById( 'loginBtn' ).onclick = function(){
var loginLayer = createSingleLoginLayer();
loginLayer.style.display = 'block';
};