专栏名称: SegmentFault思否
SegmentFault (www.sf.gg)开发者社区,是中国年轻开发者喜爱的极客社区,我们为开发者提供最纯粹的技术交流和分享平台。
目录
相关文章推荐
程序猿  ·  41岁DeepMind天才科学家去世:长期受 ... ·  14 小时前  
程序员的那些事  ·  清华大学:DeepSeek + ... ·  昨天  
OSC开源社区  ·  宇树王兴兴早年创业分享引围观 ·  2 天前  
OSC开源社区  ·  2024: 大模型背景下知识图谱的理性回归 ·  2 天前  
程序猿  ·  “我真的受够了Ubuntu!” ·  2 天前  
51好读  ›  专栏  ›  SegmentFault思否

JavaScript 设计模式 - 单例模式

SegmentFault思否  · 公众号  · 程序员  · 2019-11-27 18:50

正文

本文原载于 SegmentFault 社区专栏:前端学习
作者: li uxuan




什么是单例模式



单例模式(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 变量,三下五除二即可实现。

完整代码


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>单例模式全局Loadingtitle>
  <style>
    .loading {
      width100vw






请到「今天看啥」查看全文