Skip to content

单例模式

概念

单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点

标准单例实现方案

实现的思路是:构造函数,在自身上创建一个变量,或者闭包,记录是否已经创建实例了

ES5

javascript
function User(name) {
    if (!User._instance) {
		this.name = name
        User._instance = this
    }
    return User._instance
}

const u1 = new User('1')
const u2 = new User('2')
console.log(u1 === u2, u1.name, u2.name) // true 1 1

ES6

javascript
class User{
    constructor(name) {
		if (!User._instance) {
			this.name = name
            User._instance = this
        }
        return User._instance
    }
}

const u1 = new User('1')
const u2 = new User('2')
console.log(u1 === u2, u1.name, u2.name) // true 1 1

闭包

javascript
const User = (function() {
    let _instance
    function _User(name) {
		this.name = name
    }
    
    return function (name) {
		if (!_instance) {
			_instance = new _User(name)
        }
        return _instance
    }
})()

const u1 = new User('1')
const u2 = new User('2')
console.log(u1 === u2, u1.name, u2.name) // true 1 1

基于闭包思路,构造一个通用模式

javascript
function Single(cons) {
	let _instance
    return function(...arg) {
		if (!_instance) {
            _instance = new cons(...arg)
        }
        return _instance
    }
}

function User(name) {
    this.name = name
}

const SingleUser = Single(User)
const u1 = new SingleUser('1')
const u2 = new SingleUser('2')
console.log(u1 === u2, u1.name, u2.name) // true 1 1

还有一种,导出 getInstance 方法的思路

javascript
// es5
function User(name) {
    this.name = name
}

User.getInstance  = (function() {
    let instance
    return function(name) {
		if (!instance) {
			instance = new User(name)
        }
        return instance
    }
})()
javascript
// es6
class User {
	constructor(name) {
		this.name = name
    }
    
    static getInstance() {
        if (!User._instance) {
            User._instance = new User()
        }
        return User._instance
    }
}

JavaScript 里的单例

单例模式的核心:确保只有一个实例,并提供全局访问

  • 上面借鉴其他语言,实现的标准单例
  • es module,module 导出访问的入口,第一次被 import 时,才执行,后面 import 会引用同样的入口
  • 创建一个全局变量,并提供一个全局访问的入口(但非常不推荐,污染全局)

应用

vuex

利用 es module,实现注册的单例

javascript
// src/store.js

let Vue;

export class Store {
    constructor(options = {}) {
        if (!Vue && typeof window !== 'undefined' && window.Vue) {
          install(window.Vue)
        }
    }
}

export function install (_Vue) {
  if (Vue && _Vue === Vue) {
    if (__DEV__) {
      console.error(
        '[vuex] already installed. Vue.use(Vuex) should be called only once.'
      )
    }
    return
  }
 
  Vue = _Vue
  applyMixin(Vue)
}

全屏弹窗

javascript
class FullLoading {
    constructor() {
        if (!FullLoading.instance) {
			const dom = document.createElement('div')
            dom.style.zIndex = '999'
            dom.style.width = '100vw'
            dom.style.height = '100vh'
            dom.style.backgroundColor = 'rgba(0, 0, 0, 0.4)'
            dom.style.display = 'none'
            dom.style.lineHeight = '100vh'
            dom.style.textAlign = 'center'

            dom.innerHTML = 'loading...'

            document.body.appendChild(dom)
            this.dom = dom
            FullLoading.instance = this
        }
        return FullLoading.instance
    }
    
    static getInstance() {
        if (!FullLoading.instance) {
            FullLoading.instance = new FullLoading()
        }
        return FullLoading.instance
    }
    
    open() {
        this.dom.style.display = 'block'
    }
    
    close() {
        this.dom.style.display = 'none'
    }
}

可以通过 new 创建,也可以通过 getInstance 获取,拿到的都是同一个 instance

javascript
new FullLoading().open()

setTimeout(() => {
    FullLoading.getInstance().close()
}, 2000)