定义
保证一个类仅有一个实例,并提供一个全局唯一的访问点
使用场景
单例模式可以用来管理全局状态和共享资源,比如登陆弹窗
单例模式的几种实现方法
单例模式的实现并不困难,从它的定义中我们就可以凭借直觉推断出实现方法,即可以通过一个变量标识来辨别该类是否已经创建。 下面我们通过代码来实现:
let singAlert = function(title, show = false){
this.title = title
this.show = show
}
singAlert.instance = null
singAlert.prototype.open = function(){
this.show = !this.show
}
singAlert.getInstance = function(title){
if(!this.instance){
this.instance = new singAlert(title)
}
return this.instance
}
let a = singAlert.getInstance('弹窗一')
let b = singAlert.getInstance('弹窗二')
console.log(a === b) //true
通过singAlert.getInstance方法就可以获取singAlert的唯一实例。缺点就在于使用方法上的不透明,该函数的使用者必须清楚的知道需要通过getInstance方法才可以获取实例。除了方法的封装者知道外,其他人很难了解到这一点。
针对这个缺点我们可以通过IIFE来改进,让该函数看起来更加直观,使用起来更方便。
let singAlert = (function(){
let instance = null
let div = null
let singAlert = function(title, show = false){
if(!instance){
this.init(title, show)
instance = this
}
return instance
}
singAlert.prototype.init = function(title, show){
this.show = show
this.title = title
div = document.createElement('div')
div.innerHTML = this.title
div.style.display = this.show ? 'block' : 'none'
document.body.appendChild(div)
}
singAlert.prototype.open = function(){
this.show = true
}
singAlert.prototype.hide = function(){
this.show = false
}
return singAlert
})()
let a = new singAlert('弹窗一')
let b = new singAlert('弹窗二')
console.log(a === b) //true
上述代码虽然完成了单例模式,但是仍然存在一些不足,一个函数内既有创建对象也存在着dom操作,这显然违反了我们的单一职责原则。 现在我们可以将两个逻辑进行拆分抽离:
// 对单例的管理进行封装
let getSingleInstance = function(fn){
let result
return function(){
if(!result){
result = fn.apply(this, arguments)
}
return result
}
}
//dom操作
let createDiv = function(title, show){
let div = document.createElement('div')
div.innerHTML = title
div.id = 'btn'
div.style.display = show ? 'block' : 'none'
document.body.appendChild(div)
return div
}
// getSingleInstance负责单例管理,createDiv负责dom创建,两者合并就构成了一个单例模式。
// getSingleInstance负责接收一个函数,并对该函数进行单例模式的管理,将其返回值进行返回
let createSingleInstance = getSingleInstance(createDiv)
let bindAlertDiv = createSingleInstance(title='弹窗一', show=true)
//接下来可以对其他的操作方法以及事件进行绑定封装
function open(div){
div.style.display = 'block'
}
function hide(div){
div.style.display = 'none'
}
//绑定点击事件
document.getElementById('btn').onclick = function(){
console.log('click btn...')
hide(bindAlertDiv)
}
总结
实际上如果你在日常工作中留神的话就会发现,单例模式是我们经常会碰到的一种模式。比如Vuex,一些UI组件中的Message组件等。