Singleton Design patterns are simple to understand. A class that allows only one object (or instance) to be created is a singleton class. This design pattern is called the singleton design pattern, or singleton for short.

Implement a globally unique Modal float window

Assuming that we are WebQQ developers, when clicking the QQ avatar in the left navigation, a floating window will pop up. Obviously, this floating window is always unique in the page, and it is impossible to have two login Windows at the same time.

Traditional object-oriented singleton pattern

Implementing a standard singleton pattern is nothing more than a variable that indicates whether an object has been created for a class and, if so, returns the previously created object the next time an instance of the class is fetched. The code is as follows:

class SingletonLoginLayer {
  static getInstance() {
    // Check whether an instance has been created
    if(! SingletonLoginLayer.instance) {// If the unique instance does not exist, create it first
      SingletonLoginLayer.instance = new SingletonLoginLayer()
    }
    // If the unique instance already exists, return it directly
    return SingletonLoginLayer.instance
  }
  constructor() {
  	this.div = document.createElement( 'div' );
    this.div.innerHTML = 'I'm logging in to the floating window.';
    document.body.appendChild( this.div ); }}document.getElementById( 'loginBtn' ).onclick = function(){
  var loginLayer = SingletonLoginLayer.getInstance().div;
};
Copy the code

We through SingletonLoginLayer getInstance to obtain SingletonLoginLayer class the only object, this method is relatively simple, but there is a problem, To increase the opacity of this class, users of the SingletonLoginLayer class must know that this is a singleton class.

Our goal now is to implement a “transparent” singleton class from which users can create objects just like any other normal class. First in the CreateLoginLayer constructor, remove the code responsible for managing the singleton and make it a normal div class:

class CreateLoginLayer {
  constructor() {
  	this.div = document.createElement( 'div' );
    this.div.innerHTML = 'I'm logging in to the floating window.';
    document.body.appendChild( this.div ); }}Copy the code

Next introduced the proxy class proxySingletonCreateLoginLayer:

const proxySingletonCreateLoginLayer = (function() {
	let instance;
  return function() {
    if(! instance) { instance =new CreateLoginLayer();
    }
  	return instance;
  }
})()

document.getElementById( 'loginBtn' ).onclick = function(){
  let createLoginLayer = new proxySingletonCreateLoginLayer();
  let loginLayer = createLoginLayer.div;
};
Copy the code

By introducing the proxy class, we also completed a singleton pattern to write, is different from before, now we will be responsible for managing the singleton logic moved to the proxy class proxySingletonCreateLoginLayer. Thus, CreateLoginLayer becomes a common class, combining it with proxySingletonCreateLoginLayer can achieve the effect of the singleton pattern.

The singleton pattern in javascript

We can use a variable to determine whether a login float window has been created

const createLoginLayer = (function(){
  let div;
  return function(){
    if ( !div ){
      div = document.createElement( 'div'); Div. InnerHTML = 'I'm logged in to float window';document.body.appendChild( div );
    }

    return div;
  }
})();
document.getElementById( 'loginBtn' ).onclick = function(){
  let loginLayer = createLoginLayer();
};

Copy the code

In fact, the singleton pattern designed above is not elegant and has some problems.

  • This code still violates the single responsibility principle, as the logic for creating objects and managing singletons is kept inside the createLoginLayer object.
  • The next time we need to create a unique iframe, or script tag, on a page to request data across domains, we will have to do the same, almost copying the createLoginLayer function

We need to isolate the immutable parts. Regardless of the difference between creating a div and creating an iframe, the logic for managing singletons is completely abstracted, and the logic is always the same.

Now let’s take the logic of how to manage singletons out of the original code. The logic is wrapped inside the getSingle function, and the method fn that creates the object is passed dynamically as an argument:

const getSingle = function( fn ){
  let result;
  return function(){
    return result || ( result = fn.apply(this.arguments)); }};Copy the code

Next we pass the method used to create the login float window to getSingle as the fn parameter. We can pass not only createLoginLayer but also createScript, createIframe, createXhr, and so on. Then let getSingle return a new function and use a variable result to hold the result of fn’s calculation. The Result variable is never destroyed because it is inside the closure. In future requests, if result has already been assigned a value, it will return that value. The code is as follows:

const createLoginLayer = function(){
  let div = document.createElement( 'div'); Div. InnerHTML = 'I'm logged in to float window';document.body.appendChild( div );
  return div;
};

let createSingleLoginLayer = getSingle( createLoginLayer );

document.getElementById( 'loginBtn' ).onclick = function(){
  let loginLayer = createSingleLoginLayer();
};
Copy the code

conclusion

In the getSinge function, the concepts of closures and higher-order functions are actually mentioned as well. The singleton pattern is a simple but very useful pattern, especially the lazy singleton technique, where objects are created when appropriate and only one is created. Even more wonderful, the responsibility for creating objects and managing singletons is split between two different methods, which together have the power of the singleton pattern.