preface
What should be your first impression of a design pattern
In the course of the development of JavaScript, the pioneers have summed up many solutions to specific problems from practice. Simply put, a design pattern is a concise and elegant solution to a given problem in a given situation
Over the next few days, I’ll document various common design patterns in JavaScript. Maybe you’re already familiar with it, maybe you use it on a regular basis, but you’re not familiar with the concept, or maybe you just have a vague idea about it. Then, I believe that this series will bring you some harvest
By default, you have at least mastered these common patterns
- this
- closure
- Higher-order functions
- Prototype and prototype chain
If there are mistakes in the article, please also see friends more advice, thanks in advance
Let’s start with the singleton pattern
concept
As the name implies, there is only one instance
Definition: Ensuring that a class has only one instance and provides a global access point to access it, does the concept of global variables spring to mind? Global variables are undeniably consistent with the concept of the singleton pattern. However, we generally do not and should not use it as a singleton for two reasons:
- Global naming pollution
- Not easy to maintain, easy to override
Before ES6, we used to simulate a class using a constructor. Now we can create a class directly using the class keyword, even though it is essentially a prototype
To ensure that there is only one instance of a class, we need to provide a variable to indicate whether an instance of a class has already been created. So the core of the singleton pattern is to ensure that there is only one instance and provide global access
Around this core, we also have a basic understanding of the singleton pattern implementation mode implementation base version According to the singleton pattern definition, we can use the following simple implementation
implementation
Basic version
According to the definition of the singleton pattern, we can simply implement it in the following way
var Singleton = function(name){this.name = name} singleton. instance = null // Initialize a variable singleton. getInstance =function(name) {// Determine if the variable has been assigned, if not, make it an instantiated object of the constructorif(! this.instance) { this.instance = new Singleton(name) }return this.instance
}
var a = Singleton.getInstance('Tadpole')
var b = Singleton.getInstance('Amy')
a === b // trueCopy the code
The above code clearly reflects the definition of the singleton pattern. Only one instance is initialized by way of an intermediate variable, so a and B are exactly equal in the end
We can also use the ES6 class keyword to do this
Class Singleton {constructor(name){this.name = name this.instance = null} class Singleton {constructor(name){this.name = name this.instance = null}if(! this.instance) { this.instance = new Singleton(name) }return this.instance
}
}Copy the code
It’s not hard to see how ES6 is implemented in the same way that we implemented it through constructors
Problem: not transparent enough, we need to restrict how class instantiations are called
The coupling of functional business code together is not conducive to later maintenance of the constructor. Let’s make a simple change to the above approach
// Attach the variable directly to the constructor and return itfunction Singleton(name) {
if(typeof Singleton.instance === 'object') {
returnSingleton.instance} // Create instance this.name = namereturn Singleton.instance = this
}
var a = new Singleton('Tadpole')
var b = new Singleton('Amy')Copy the code
The new keyword can be used to initialize instances, but at the same time, there are new problems
- Judging the Single. Instance type to return, you may not get the expected result
- The coupling is too high
Class Singleton {constructor(name) {this.name = nameif(! Singleton.instance) { Singleton.instance = this }return Singleton.instance
}
}Copy the code
closure
By defining the singleton pattern, you want to ensure that there is only one instance and that global access is available. Closures, then, can certainly do the same
var Singleton = (function () {
var SingleClass = function () {};
var instance;
return function () {
if(! Instance) {instance = new SingleClass() // New SingleClass()}return instance;
}
})()Copy the code
By virtue of closures, a variable is saved and eventually returned, providing global access
Again, the above code does not solve the coupling problem
Let’s take a closer look at this piece of code and see if we can achieve a separation of functionality if we pull out the constructor part of it
The proxy implementation
Modify the above code
function Singleton(name) {
this.name = name
}
var proxySingle = (function(){
var instance
return function(name) {
if(! instance) { instance = new Singleton(name) }return instance
}
})()Copy the code
Take the steps to create functions out of the functions, and move the logic responsible for managing singletons into proxySingle. The purpose of this is to turn the Singleton class into a normal class in which we can write some business logic separately, thus achieving the effect of logic separation
We have now achieved the effect of logical separation and opacity. But does the class responsible for the proxy already meet our requirements? The answer is no. Imagine if our constructor had more than one argument, and we should have represented it in the proxy class
So, is there a more general way to do it
Generic lazy singleton
In the last few rounds, we’ve pretty much finished creating the singleton pattern. Now we need to find a more general way to solve the problems left behind
Imagine if we had a function as an argument
// Pass the function as an argument var Singleton =function(fn) {
var instance
return function() {// Collect parameters using apply and return the result by executing the parameters passed inreturn instance || (instance = fn.apply(this, arguments))
}
}Copy the code
The biggest advantage of this approach is that we cache the results we want and only call them when we need them, fulfilling the single responsibility of encapsulation
application
As mentioned earlier, all patterns are derived from practice, so let’s take a look at how they are used in real development
From the definition of the singleton pattern, it is not difficult to think of its practical use in development, such as: global mask layer
A global mask layer cannot be created every time it is called. The best way to create it is to create it only once, save it with a variable, and return the result when called again
// Create a mask layerfunction () {
var div = document.createElement('div')
div.style.width = '100vw'
div.style.height = '100vh'
div.style.backgroundColor = 'red'
document.body.appendChild(div)
returnVar createSingleLayer = Singleton(createDiv) document.getelementById ('btn').onclick = functionVar divLayer = createSingleLayer()}Copy the code
Of course, there are many scenarios that are applicable in practice, such as login boxes and state management tools like Vux that we might use, which actually fit the singleton pattern
Afterword.
The singleton pattern is a simple and practical pattern, and we can create many practical and elegant applications by creating objects and managing singleton. Of course, it also has its own disadvantages, such as only one instance ~
Only when it is properly used can it bring out its maximum power