Singleton pattern
Singleton pattern, also known as singleton pattern, is a common software design pattern. When applying this pattern, the class of a singleton object must ensure that only one instance exists.
From Wikipedia
A typical application is to click the login button and pop up the login floating window. No matter how many times the login button is clicked, the page will always pop up only one login floating window.
Implementing the singleton pattern
The idea is simple. Use a variable to record whether an object has been created for a class, if not, return the newly created object, and vice versa, return the previously created object.
Record instances in the constructor:
var Singleton = function(name) { this.name = name; this.instance = null; }; Singleton.prototype.getName = function() { console.log(this.name); }; Singleton.getInstance = function(name) { if (!this.instance) { this.instance = new Singleton(name); } return this.instance; }; var a = Singleton.getInstance('sean1'); var b = Singleton.getInstance('sean2'); console.log(a === b); // true
Record instances using closures:
var Singleton = function(name) { this.name = name; }; Singleton.prototype.getName = function() { console.log(this.name); }; Singleton.getInstance = (function() { var instance = null; return function(name) { if (!instance) { instance = new Singleton(name); } return instance; }; })(); var a = Singleton.getInstance('sean1'); var b = Singleton.getInstance('sean2'); console.log(a === b); // true
The above method is relatively simple, but users of the Singleton class have to know that it is a singleton class and that they need to call the getInstance() function (not the new way) to get the object, which adds "opacity".
Transparent singleton mode
An IIFE is used to form a closure, in which the instance is recorded by the variable instance, and the constructor is returned.
var CreateDiv = (function() { var instance; var CreateDiv = function(html) { if (instance) { return instance; } this.html = html; this.init(); return (instance = this); }; CreateDiv.prototype.init = function() { var div = document.createElement('div'); div.innerHtml = this.html; document.body.appendChild(div); }; return CreateDiv; })(); var a = new CreateDiv('sean1'); var b = new CreateDiv('sean2'); console.log(a === b); // true
A transparent singleton class has been written, but there are still shortcomings, which increase the complexity of some programs and make them less readable. Another important point is that it violates the "single responsibility principle" and then improves it.
Implementing singleton pattern with proxy
In fact, it is to separate the actual business code from the code responsible for managing the singleton. The class that manages the singleton is the proxy class.
var CreateDiv = function(html) { this.html = html; this.init(); }; CreateDiv.prototype.init = function() { var div = document.createElement('div'); div.innerHtml = this.html; document.body.appendChild(div); }; var ProxySingletonCreateDiv = (function() { var instance; return function(html) { if (!instance) { instance = new CreateDiv(html); } return instance; }; })(); var a1 = new ProxySingletonCreateDiv('sean1'); var b1 = new ProxySingletonCreateDiv('sean2'); console.log(a1 === b1); // true // If you want to create multiple div s, call CreateDiv directly var a2 = new CreateDiv('sean1'); var b2 = new CreateDiv('sean2'); console.log(a2 === b2); // false
Singleton pattern in JavaScript
The implementations mentioned above are closer to those in traditional object-oriented languages, but JavaScript is a class-free language. We only need to remember the singleton pattern, but the core is to ensure that there is only one instance and provide global access.
Global variables are not singleton patterns, but they satisfy singleton conditions, so we often use global variables as singletons: var a = {};;. But we all know that global variables can easily lead to namespace pollution and inadvertent coverage. It is necessary to minimize the use of global variables, even if necessary, to minimize pollution:
-
using namespace std
var namespace1 = { a: function() { return 1; }, b: function() { return 2; } }; // Or create namespaces dynamically: var MyApp = {}; MyApp.namespace = function(name) { var parts = name.split('.'); var current = MyApp; for (var item of parts) { if (!current[item]) { current[item] = {}; } current = current[item]; } }; MyApp.namespace('event'); MyApp.namespace('dom.style'); console.dir(MyApp);
-
Encapsulating private variables with closures
var user = (function() { var __name = 'sean', __age = 19; return { getUserInfo: function() { return __name + '-' + __age; } }; })();
Inert singleton
The so-called inertia is to do it only when needed. There are also implementations that satisfy laziness mentioned earlier, but based on "classes". Let's look at the typical application implementation mentioned at the beginning of this article:
<html> <body> <button id="loginBtn">Sign in</button> <script> var createLoginLayer = (function() { var div; return function() { if (!div) { div = document.createElement('div'); div.innerHTML = 'I'm the login floating window.'; div.style.display = 'none'; document.body.appendChild(div); } return div; }; })(); document.getElementById('loginBtn').onclick = function() { var loginLayer = createLoginLayer(); loginLayer.style.display = 'block'; }; </script> </body> </html>
The above code completes the implementation of the lazy singleton, but still violates the single responsibility principle. If we do not create a login floating window next time, but other elements, please look back at the generic lazy singleton.
General inert singleton
// Define a function to manage a singleton: var getSingle = function(fn) { var result; return function() { return result || (result = fn.apply(this, arguments)) } } // Define the function to create a floating window: var createLoginLayer = function() { var div = document.createElement('div'); div.innerHTML = 'I'm the login floating window.'; div.style.display = 'none'; document.body.appendChild(div); return div; } // Get the floating window of a singleton var createSingleLoginLayer = getSingle(createLoginLayer); document.getElementById('btn').onclick = function() { var loginLayer = createSingleLoginLayer(); loginLayer.style.display = 'block'; };
Reference resources
Zeng Tan. JavaScript Design Patterns and Development Practice (Turing Originality)