【JS设计模式】之1 单例模式

393 阅读2分钟

【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 排版