JavaScript Design pattern part 1: Singleton pattern

Keywords: Javascript Vue React IE

What is singleton mode

Singleton Pattern ensures that a class has only one instance and provides a global access point to access it

Singleton pattern is a simple and easy to understand design pattern, but its usage scenarios are very wide, including the famous Vuex also uses singleton pattern.

This article introduces two implementation methods of singleton pattern: class and closure. It also introduces singleton pattern in Vuex.

Realization way

Class is a new syntax of ES6. Before we want to create a new object instance, it is implemented through the new constructor. Every time we call new, we will generate a new instance object. Each instance object is completely independent.

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

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

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

So let's think about how to return the same instance object every time new?

There must be a variable to save the instance object generated by new for the first time. When new is executed later, it will directly return the instance object generated for the first time, thus realizing a single instance.

Let's practice it in two ways: classes and closures.

Class implementation

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

Class is the prototype of an instance. All methods defined in the class will be inherited by the instance. If the static keyword is added before a method, it means that the method will not be inherited by the instance, but called directly through the class, which is called "static method".

Static methods can be called directly on the parent class SingletonCar.getInstance(), rather than on the instance object. If you call a static method on an instance, an error is thrown indicating that the method does not exist.

Class is used to implement singleton mode. Just remember the static getInstance() method.

Closure implementation

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

With the help of closures, the instance variable is reserved in memory and will not be garbage collected. It is used to save the only instance. When new is called multiple times, only the first instance created is returned.

Singleton mode in Vuex

Vuex is the state management class library of Vue, similar to Redux to React. In fact, their ideas are all from the Flux architecture. They use a global Store to Store all the states of the application, and then provide some API s for users to read and modify. As soon as you see the globally unique Store, you can think of the singleton mode.

How to reference Vuex

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

Vue.use(Vuex)

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

Vuex is a plug-in. You can install the plug-in by calling the Vue.use() method. For details, please refer to Official documents

Vuex internally implements an install method, which will be called when Vue.use(), and Vue will be passed into the install method as a parameter to inject the Store into Vue. But if you have tried to call Vue.use(Vuex) multiple times in the development environment, you will know that the browser will report an error. Next, let's look at the internal implementation of vuex.

How does Vuex guarantee a unique Store

Part of the source code of upper Vuex:

let Vue

...

export function install (_Vue) {
  // Whether Vue.use(Vuex) has been executed. If it is executed multiple times in a non production environment, an error will be prompted
  if (Vue && _Vue === Vue) {
    if (process.env.NODE_ENV !== 'production') {
      console.error(
        '[vuex] already installed. Vue.use(Vuex) should be called only once.'
      )
    }
    return
  }
  // If Vue.use(Vuex) is executed for the first time, the incoming Vue is assigned to the defined variable Vue
  Vue = _Vue
  // Vuex initialization logic
  applyMixin(Vue)
}

The full source path is in vuex/src/store.js

We can see that in Vuex, a variable Vue is defined first. Note that this is not the real Vue, but a variable, also called Vue.

When Vue.use(Vuex), the install method will be called, and the real Vue will be passed in as a parameter. If you execute Vue.use(Vuex) multiple times, it will only take effect once, that is, applyMixin(Vue) will only be executed once, so there will only be a unique Store, which is the implementation of the singleton mode in Vuex.

Practice

Implement a globally unique Loading mask.

thinking

In the process of business development, there are many requirements with Loading status. At this time, we will directly take out the single instance mode, and remember the above getInstance static method or closure instance variable, which can be implemented by dividing three times, five times and two times.

Complete code

<!DOCTYPE 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>Single mode Global Loading</title>
  <style>
    .loading {
      width: 100vw;
      height: 100vh;
      line-height: 100vh;
      position: absolute;
      top: 0;
      left: 0;
      background-color: #000;
      opacity: .7;
      color: #fff;
      text-align: center;
    }
  </style>
</head>
<body>
  <button id="startLoading">Click load</button>

  <script>
    const Loading = (function () {
      let instance

      return function () {
        if (!instance) {
          instance = document.createElement('div')
          instance.innerHTML = 'Loading...'
          instance.id = 'loading'
          instance.className = 'loading'
          document.body.appendChild(instance)
        }
        return instance
      }
    })()

    document.getElementById('startLoading').addEventListener('click', () => {
      const loading = new Loading()
    })
  </script>
</body>
</html>

summary

The singleton mode is to ensure that a class has only one instance. If it is implemented by class, remember the getInstance static method. If it is implemented by closure, remember the instance variable. When we are in business development, when we need a globally unique state like Vuex, it is when the singleton mode is launched.

Posted by ztealmax on Sun, 24 Nov 2019 07:24:59 -0800