单例模式

114 阅读2分钟

什么是单例模式

一句话介绍:保证类的实例只有一个单例模式更适用于重复访问的模块,保存其缓冲,避免重复调用和加载。 例如,我们的整个系统,有时需要一个唯一的全局变量,用来统一全局的配置和某些通用数据。这种模式有很广泛的实践,如 Vuex中对单例模式的使用。

如何实现

javascript中,实现一个单例模式可以用一个变量来标志当前的类已经创建过对象,如果下次获取当前类的实例时,直接返回之前创建的对象即可,如下:

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

CreateSingletion.prototype.getname = function () {
    return this.name
}

const Singleton = function () {
    let instance = null
    return function (name) {
        return instance || (instance = new CreateSingletion(name)) 
        // 如果已经存在实例, 则返回存在的实例, 否则创建一个实例并赋值给result , 
        // 下次调用时直接返回创建的实例
    }
}()


const single1 = new Singleton('张三')
const single2 = new Singleton('李四')
console.log(single1, 'single1');
console.log(single2, 'single2');
console.log(Object.is(single1,single2));

打印输出结果

image.png

## 在哪些场景会用到?

1.Vuex

很典型的单例模式的应用。Vuex的store就是一个单例模式。采用了单一状态树,一个对象即为唯一数据源

// 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 (process.env.NODE_ENV !== 'production') {
      console.error(
        '[vuex] already installed. Vue.use(Vuex) should be called only once.'
      )
    }
    return
  }
  Vue = _Vue

  applyMixin(Vue)
}

Store的初始化时,只会执行一次install方法。在install方法中,会将Vue赋值,并将vuex的相关逻辑绑定到Vue实例上。可以看到Vuexinstall的时候,判断是否有了唯一的数据。

2.弹框

这种实现称为惰性单例,意图解决需要时才创建类实例对象

<div id="loginBtn">弹窗</div>

function createLoginLayer() {
    const div = document.createElement('div')
    div.innerHTML = '我是浮窗';
    div.style.display = 'none';
    document.body.appendChild(div);
    return div;
}

const singleLoginLayer = function (fn) {
    let result = null
    return function () {
        return result || (result = fn.apply(this, arguments))
    }
}

const createSingleLayer = singleLoginLayer(createLoginLayer)

document.querySelector('#loginBtn').addEventListener('click', () => {
    // 单例模式, 每次调用只有一个
    const loginLayerDom1 = createSingleLayer()
    const loginLayerDom2 = createSingleLayer()
    console.log(Object.is(loginLayerDom1, loginLayerDom2));  // 返回true
    loginLayerDom1.style.display = 'block'
    loginLayerDom2.style.display = 'block'
})

在使用单例模式的情况下,多次调用createSingleLayer()只会返回一个实例对象

image.png

image.png

在不使用单例模式的情况下,多次调用createLoginLayer()返回多个实例对象

document.querySelector('#loginBtn').addEventListener('click', () => {
    // 不是单例模式的情况下, 多次调用就显示多个
     const noSingleDom1 = createLoginLayer()
     const noSingleDom2 = createLoginLayer()
     console.log(Object.is(noSingleDom1, noSingleDom2));  // 返回false
     noSingleDom1.style.display = 'block'
     noSingleDom2.style.display = 'block'
})

image.png

image.png