一.什么是单例模式
单例模式顾名思义是在该模式下,函数调用时如果实例没有创建就新创建一个实例,再次调用该函数返回已经创建的实例。
二.单例模式的通用实现
在实现单例的过程中,我们通常使用两个函数,第一个函数来实现单例控制逻辑,第二个函数用来创建实例
// 管理单例
function getSingleton(fn) {
let instance;
return function() {
return instance || (instance = fn.apply(this, arguments))
}
}
// 创建弹窗
function createModal(option = {}) {
let div = document.createElement('div')
div.innerHTML = option.text || '弹窗'
div.className = 'modal'
div.style.display = 'none'
document.body.appendChild(div)
// 这里不直接return div 而是返回一个对象,只暴露想要暴露的
return {
option,
show() {
div.style.display = 'block'
},
}
}
// 创建弹窗单例
let createSingletonModal = getSingleton(createModal)
document.querySelector('.modal-btn').addEventListener('click', () => {
createSingletonModal({text: '我是弹窗'}).show()
setTimeout(() => createSingletonModal({text: '我是弹窗2'}).show(), 9000);
})
上述实现了单例的功能,但是仍然有缺陷,在实际的项目中,modal框每次弹出的内容可能都会有所不同,对上述方法进行改进,暴露出
setContent
方法,重新设置option
// 管理单例
function getSingleton(fn) {
let instance;
return function () {
return instance &&
instance.setContent.apply(instance, arguments) ||
(instance = fn.apply(this, arguments))
}
}
// 创建弹窗
function createModal(option = {}) {
let div = document.createElement('div')
div.innerHTML = option.text || '弹窗'
div.className = 'modal'
div.style.display = 'none'
document.body.appendChild(div)
return {
option,
show() {
div.style.display = 'block'
},
setContent(option) {
if (div.innerText === option.text) return this
div.innerHTML = option.text || '弹窗'
return this
}
}
}
// 创建弹窗单例
let createSingletonModal = getSingleton(createModal);
document.querySelector('.modal-btn').addEventListener('click', () => {
let m1 = createSingletonModal({ text: '我是弹窗1' })
m1.show()
setTimeout(() => {
let m2 = createSingletonModal({ text: '我是弹窗2' })
m2.show()
console.log(m1 === m2); // true
}, 3000);
})
三.总结
本文首先介绍了通用单例实现,以页面modal为例,在首次实现中,我们发现对于不同的text,弹窗显示的值都是相同的,因此我们提供了一个setContent
方法来实现内容的更新。
但这样做也有一点缺陷,就是getSingleton
方法本义只是控制单例逻辑的实现,但修改后还调用单例内部的方法用来实现视图的更新
todo: 用一种更优雅的方法来实现视图的更新,在
getSingleton
不调用setContent
方法来实现。