JavaScript设计模式第1篇:单例模式

什么是单例模式

单例模式(Singleton Pattern)确保一个类只有一个实例,并提供一个访问它的全局访问点

单例模式是设计模式中较为简单,好理解的一种模式,但是它的使用场景是很广泛的,包括大名鼎鼎的 Vuex 也使用了单例模式。

本文会介绍单例模式的两种实现方法:类和闭包,同时也会对Vuex中的单例模式进行介绍。

实现方式

类Class是ES6新增的语法,在之前我们想要新建一个对象实例,是通过new构造函数的方式来实现的。我们每次调用new的时候,都会生成一个新的实例对象,每个实例对象之间是完全独立的。

function Car (name) {
    this.name = name;
}

var car1 = new Car('benz');
var car2 = new Car('audi');

console.log(car1 === car2)      // false

那么我们来思考一下,怎么才能每次new都返回同一个实例对象呢?

肯定是有一个变量将第一次new生成的实例对象保存了下来,后面再执行new的时候,就直接返回第一次生成的实例对象,这样就实现了单例。

我们通过两种方法来实践一下:类和闭包。

类实现

class SingletonCar {
    constructor () {
        this.name = 'benz';
    }
    static getInstance () {
        if (!SingletonCar.instance) {
            SingletonCar.instance = new SingletonCar();
        }
        return SingletonCar.instance;
    }
}

let car1 = SingletonCar.getInstance();
let car2 = SingletonCar.getInstance();

console.log(car1 === car2)      // true

类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。

静态方法可以直接在父类上调用SingletonCar.getInstance(),而不是在实例对象上调用。如果在实例上调用静态方法,会抛出一个错误,表示不存在该方法。

用类来实现单例模式,只要记住这个getInstance()静态方法就可以了。

闭包实现

var SingletonCar = (function () {
    var instance;
    
    var SingletonCarTemp = function () {
        this.name = 'benz';
    };
    
    return function () {
        if (!instance) {
            instance = new SingletonCarTemp();
        }
        return instance;
    }
})();

var car1 = new SingletonCar();
var car2 = new SingletonCar();

console.log(car1 === car2)      // true

借助闭包,在内存中保留了 instance 变量,不会被垃圾回收,用来保存唯一的实例,多次调用 new 的时候,只返回第一次创建的实例。

Vuex中的单例模式

Vuex 是 Vue 的状态管理类库,类似于 Redux 之于 React,其实他们的理念都是来自于 Flux 架构,用一个全局的 Store 存储应用所有的状态,然后提供一些 API 供用户去读取和修改。一看到全局唯一的 Store,就可以想到是单例模式了。

如何引用 Vuex

import Vue from 'vue'
import Vuex form 'vuex'
import store from './store'

Vue.use(Vuex)

new Vue({
  el: '#app',
  store
})

Vuex 是一个插件,通过调用 Vue.use() 方法可以安装这个插件,具体 Vue 插件的写法可以参考官方文档

Vuex 在内部实现了一个 install 方法,该方法会在 Vue.use() 时被调用,Vue 会被作为参数传入 install 方法中,从而把 Store 注入到 Vue 中。但是如果你试过在开发环境多次调用 Vue.use(Vuex) 的话,就会知道是浏览器是会报错的,接下来我们看一下 Vuex 的内部实现。

Vuex 如何保证唯一 Store

上 Vuex 部分源码:

let Vue

...

export function install (_Vue) {
  // 是否已经执行过了 Vue.use(Vuex),如果在非生产环境多次执行,则提示错误
  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.use(Vuex),则把传入的 _Vue 赋值给定义的变量 Vue
  Vue = _Vue
  // Vuex 初始化逻辑
  applyMixin(Vue)
}

完整源码路径在 vuex/src/store.js

我们可以看到,在 Vuex 内部,先定义了一个变量 Vue,注意这里不是真正的 Vue,只是一个变量,也叫 Vue。

在 Vue.use(Vuex) 的时候,会调用 install 方法,真正的 Vue 会被当做参数传入,如果多次执行 Vue.use(Vuex),也只会生效一次,也就是只会执行一次 applyMixin(Vue),所以只会有一份唯一的 Store,这就是 Vuex 中单例模式的实现。

练习

实现一个全局唯一的 Loading 遮罩。

思路

我们在业务开发的过程中,有很多需求都会有 Loading 状态,这时候直接掏出单例模式,记住上面的 getInstance 静态方法或者闭包 instance 变量,三下五除二即可实现。

完整代码




  
  
  
  单例模式全局Loading
  


  

  

总结

单例模式,就是确保一个类只有一个实例,如果采用 class 来实现,记住 getInstance 静态方法,如果采用闭包来实现,记住 instance 变量。当我们在业务开发中,遇到类似于 Vuex 这种需要全局唯一状态的时候,就是单例模式登场的时候。

你可能感兴趣的:(javascript,设计模式,单例模式)