define

A class can only be instantiated once, a class has only one instance, and provides a global access point to access it. For a single object, only one object instance is generated, avoiding frequent creation and destruction of instances and reducing memory usage. Do not apply to scenarios where you need to dynamically extend objects or create multiple similar objects.

The principle of

Use a variable to store the class instance object. The initial value is null or undefined. During class instantiation, the system first checks whether the class instance object exists. If the class instance exists, the system returns the instance. If the class instance does not exist, the system returns after creating the class instance. No matter how many times the class generated instance method is called, the same instance object is returned.

The class diagram

Simple singleton pattern

A class can create multiple instances, and none of them are equal

class Window {
  constructor(){}}let w1 = new Window();

let w2 = new Window();

// The two instances are not equal
console.log(w1 === w2); //false
Copy the code

So what do we want to implement singletons by making constructor private so that the constructor can’t be accessed externally? Instead of creating class instance objects through the traditional new operator, define a getInstance() method in the SingleObject function to manage singletons and create return class instance objects. The first call to window.getInstance () has no value, so initialize a value and return it. The second time the value is returned directly. This prevents you from creating two instances of Window.

Es6 implements the singleton mode

Properties in TS have three modifiers: *public (default), which can be changed in classes, subclasses, and objects *protected, which can be changed in classes and subclasses *private, which can be changed in classes
class Window {
  private static instance: Window;
  private constructor() {}
  public static getInstance() {
    if(! Window.instance) { Window.instance =new Window();
    }
    returnWindow.instance; }}let w1 = Window.getInstance();

let w2 = Window.getInstance();

console.log(w1 === w2); //true
Copy the code

Es5 implementation of the singleton pattern

interface Window {
    hello: any
}
function Window() { }
Window.prototype.hello = function () {
    console.log('hello');
}
Window.getInstance = (function () {
    let window: Window;
    return function () {
        if (!window)
            window = new (Window as any) ();return window;
    }
})();
let window = Window.getInstance();
window.hello();
Copy the code

The downside of this approach is that the consumer must be told to get the singleton through the getInstance () method.

Transparent singleton mode

The user doesn’t know whether to use singletons or call the constructor with new and get an error. To solve this problem, we create objects as new, but return the same instance.

let Window = (function () {
  let window: Window;
  let Window = function (this: Window) {
    if (window) {
      return window;
    } else {
      return (window = this); }}; Window.prototype.hello =function () {
    console.log('hello');
  };
  returnWindow; }) ();let window1 = new (Window as any) ();let window2 = new (Window as any) (); window1.hello();console.log(window1 === window2);//true
Copy the code

In this way, the user does not need to know how to use special, normal new will do.

Singletons are separated from builds

The singleton creation method in the above example is implemented inside the class. The operation of managing the singleton is coupled with the operation of object creation, and the function code does not comply with the single responsibility principle. So we want to separate singletons from builds.

interface Window {
    hello: any
}
function Window() {
}
Window.prototype.hello = function () {
    console.log('hello');
}

let createInstance = (function () {
    let instance: Window;
    return function () {
        if(! instance) { instance =new (Window as any) (); }return instance;
    }
})();

let window1 = createInstance();
let window2 = createInstance();
window1.hello();
console.log(window1 === window2)
Copy the code

Packaging changes

The above example createInstance can only create instances of Windows. I want createInstance to be able to create instances of any class


function Window() {

}
Window.prototype.hello = function () {
    console.log('hello');
}
let createInstance = function (Constructor: any) {
  	// Create variables of any type
    let instance: any;
    return function (this: any) {
        if(! instance) { Constructor.apply(this.arguments);
          	//this.__proto__ = Constructor.prototype
            Object.setPrototypeOf(this, Constructor.prototype)
            instance = this;
        }
        returninstance; }};let CreateWindow: any = createInstance(Window);
let window1 = new CreateWindow();
let window2 = new CreateWindow();
window1.hello();
console.log(window1 === window2)
Copy the code

The sample

Realization of modal window

<! DOCTYPEhtml>
<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>Document</title>
</head>

<body>
    <button id="show-button">Display the modal window</button>
    <button id="hide-button">Hide the modal window</button>
    <script>
        class Login {
            constructor() {
                this.element = document.createElement('div');
                this.element.innerHTML = (
                    'user name  '
                );
                this.element.style.cssText = 'width: 100px; height: 100px; position: absolute; left: 50%; top: 50%; display: block; ';
                document.body.appendChild(this.element);
            }
            show() {
                this.element.style.display = 'block';
            }
            hide() {
                this.element.style.display = 'none';
            }
        }
        Login.getInstance = (function () {
            let instance;
            return function () {
                if(! instance) { instance =new Login();
                }
                return instance;
            }
        })();

        document.getElementById('show-button').addEventListener('click'.function (event) {
            Login.getInstance().show();
        });
        document.getElementById('hide-button').addEventListener('click'.function (event) {
            Login.getInstance().hide();
        });
    </script>
</body>

</html>
Copy the code

The cache

Accessing disk files is asynchronous, so calling the readFile method every time is slow. If we have already accessed disk files, we will add the cache to memory. Memory access is much faster than disk access.

let express = require('express');
let fs = require('fs');
let app = express();
app.get('/user/:id'.function (req: any, res: any) {
  let id = req.params.id  
  fs.readFile(`./users/${id}.json`.'utf8'.function (err: any, data: any) {
    let user = JSON.parse(data);
    res.json(user);
  });
});
app.listen(3000);
Copy the code

The cache must be singleton so that no matter who stores it, others can get it

let express = require('express');
let fs = require('fs');
/ / cache
let cache: Record<any.any> = {};
let app = express();
app.get('/user/:id'.function (req: any, res: any) {
    let id = req.params.id;
  	// If there is one in the cache, return it directly or access disk
    let user = cache.get(id);
    if (user) {
        res.json(user);
    } else {
        fs.readFile(`./users/${id}.json`.'utf8'.function (err: any, data: any) {
            let user = JSON.parse(data); cache.put(id, user); res.json(user); }); }}); app.listen(3000);
Copy the code

Small partners feel helpful to you please like 👍👍 support, feel good to write please pay attention to the column

👉👉 design patterns suitable for front-end personnel

reference

Web front-end – JS design pattern