【JS设计模式】之1 单例模式
前提: js是单线程的,所以不用考虑锁的问题, 单例模式就是产生一个唯一的实例 不污染全局作用域的方式有: IEFE, 命名空间, 函数作用域,闭包
一个常见需求:点击某个按钮的时候需要在页面弹出一个遮罩层。
version1 : 每次点击的时候。创建一次
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<button id="btn">show popup </button>
<script src="./1.singleton.js"></script>
</body>
</html>
// 1.singleon.js
//需求 点击按钮出现弹框
//程序入口
function init() {
showPopup()
}
init()
//相应事件和ui
function showPopup() {
var btn = document.getElementById('btn')
btn.addEventListener('click', function() {
var popUp = new Popup()
popUp.show('hello world')
}, false)
}
//ui 类
function Popup() {}
Popup.prototype.show = function(txt) {
var _div = document.createElement('div')
_div.innerHTML = txt
document.body.appendChild(_div)
}
- 显然每次创建一个,不符合要求,而且频繁操作dom,影响性能,我们可以在这个基础上改造
version2: 声明一个变量为null ,变化的时候,判断该变量是否为空,为空初始化实例。否则直接使用实例
//程序入口
function init() {
showPopup()
}
init()
var instance = null
//相应事件和ui
function showPopup() {
var btn = document.getElementById('btn')
btn.addEventListener('click', function() {
if (!instance) {
instance = new Popup().show('hello world ')
}
}, false)
}
//ui 类
function Popup() {}
Popup.prototype.show = function(txt) {
var _div = document.createElement('div')
_div.innerHTML = txt
var ele = document.body.appendChild(_div)
return ele
}
- 缺点是使用了全局变量
version3 : 使用闭包实现
//程序入口
function init() {
showPopup()
}
init()
//增加这里
var createPopUp = function() {
var instance = null
return function() {
return instance || (instance = new Popup().show(arguments[0]))
}
}()
//相应事件和ui
function showPopup() {
var btn = document.getElementById('btn')
btn.addEventListener('click', function() {
//调用 + 传递参数
createPopUp('aaa')
}, false)
}
//ui 类
function Popup() {}
Popup.prototype.show = function(txt) {
var _div = document.createElement('div')
_div.innerHTML = txt
var ele = document.body.appendChild(_div)
return ele
}
上面虽然可以实现,但是js中 window 对象是唯一的, 或者购物车是唯一的,或者vuex中 store是唯一的,这些实现都要基于一种只有一个实例的思想,不够通用, 再封装一个通用的版本
version 4 : 上面变化的部分(instance = new Popup().show(arguments[0]))可以再次抽象,js中函数是一等公民 用函数可以当参数的特点,替换成函数。
//程序入口
function init() {
showPopup()
}
init()
//关键代码
//这个函数可以抽离到其他文件, 供其他需要单例的场景使用
var SingletonFactory = function(fn) {
var instance = null
return function() {
return instance || (instance = fn.apply(this, arguments))
}
}
var createPopUp = SingletonFactory(function() {
return new Popup().show(arguments[0])
})
//相应事件和ui
function showPopup() {
var btn = document.getElementById('btn')
btn.addEventListener('click', function() {
//调用
createPopUp('asddasdasd')
}, false)
}
//ui 类
function Popup() {}
Popup.prototype.show = function(txt) {
var _div = document.createElement('div')
_div.innerHTML = txt
var ele = document.body.appendChild(_div)
alert(1)
return ele
}
总结: 单例模式就是通过闭包以及函数可以作为参数的特性,实现的保证全局只有一个类的模式。
首发于: 前端关宇 设计模式之单例模式
本文使用 mdnice 排版