# 什么是单例模式

保证一个类仅有一个实例,并提供一个访问它的全局访问点,这样的模式就是单例模式。

# 单例理模式的实现

下面我们用两种方式来实现单例模式。

第一种方式使用静态方法。

class SingleTon {
    static getInstance() {
        return !SingleTon.instance ? SingleTon.instance = new SingleTon() : SingleTon.instance
    }
}

console.log(SingleTon.getInstance() === SingleTon.getInstance()) // true

第二种方式,使用闭包。

class SingleTon {}

SingleTon.getInstance = (function() {
    let instance = null
    return function() {
        return !instance ? instance = new SingleTon() : instance 
    }
})()

console.log(SingleTon.getInstance() === SingleTon.getInstance())

# Vuex中的单例模式

下面是Vuex中install方法的简化版:

let Vue
export function install(_Vue) {
  // 判断传入的vue实例对象是否已经被install过Vuex插件
  if (Vue && _Vue === Vue) return
  // 若没有,则为这个Vue实例对象install一个唯一Vuex
  Vue = _Vue
  // 将Vuex的初始化逻辑编写进Vue的钩子函数里
  applyMixin(Vue)
}

如果Vuex的install方法中没有实现单例模式,如果我们多次调用了install方法就有可能对数据产生覆盖。

# 实战

实战一

实现Storage,使得该对象为单例,基于 localStorage 进行封装。实现方法 setItem(key,value) 和 getItem(key)

方法一:

class Storage {
    static getInstance() {
        return !Storage.instance ? Storage.instance = new Storage() : Storage.instance
    }

    getItem(key) {
        return localStorage.getItem(key)
    }

    setItem(key, value) {
        localStorage.setItem(key, value)
    }
}

方法二:

function StorageBase () {}
StorageBase.prototype.getItem = function (key){
    return localStorage.getItem(key)
}
StorageBase.prototype.setItem = function (key, value) {
    return localStorage.setItem(key, value)
}

const Storage = (function(){
    let instance = null
    return function(){
        return !instance ? instance = new StorageBase() : instance
    }
})()

实战二

实现一个全局唯一的Modal弹框

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>单例模式弹框</title>
</head>
<style>
    #modal {
        height: 200px;
        width: 200px;
        line-height: 200px;
        position: fixed;
        left: 50%;
        top: 50%;
        transform: translate(-50%, -50%);
        border: 1px solid black;
        text-align: center;
    }
</style>
<body>
	<button id='open'>打开弹框</button>
	<button id='close'>关闭弹框</button>
</body>
<script>
    // 核心逻辑,这里采用了闭包思路来实现单例模式
    const Modal = (function() {
    	let modal = null
    	return function() {
            if(!modal) {
            	modal = document.createElement('div')
            	modal.innerHTML = '我是一个全局唯一的Modal'
            	modal.id = 'modal'
            	modal.style.display = 'none'
            	document.body.appendChild(modal)
            }
            return modal
    	}
    })()
    
    // 点击打开按钮展示模态框
    document.getElementById('open').addEventListener('click', function() {
        // 未点击则不创建modal实例,避免不必要的内存占用;此处不用 new Modal 的形式调用也可以,和 Storage 同理
    	const modal = new Modal()
    	modal.style.display = 'block'
    })
    
    // 点击关闭按钮隐藏模态框
    document.getElementById('close').addEventListener('click', function() {
    	const modal = new Modal()
    	if(modal) {
    	    modal.style.display = 'none'
    	}
    })
</script>
</html>