Because the company’s project is not a first-hand project, the recent page and demand revision, so I want to optimize some pages. See a lot of if… else… Statement, so think about how to prepare for optimization with design patterns.
preface
Javascript, like other languages, also has a variety of design patterns, such as singleton, proxy, observer, policy, etc. The use of design patterns can make our code logic clearer, and easier to maintain and refactor.
This article mainly introduces the singleton pattern of JS, involving many knowledge points, including closure, scope chain, immediately execute function, etc. (if wrong, welcome to point out)
The body of the
Singleton schema definition
The singleton pattern, also known as the singleton pattern, ensures that a class corresponds to only one instance. Although JS is a weakly typed language, it also has the concept of classes. In ES5, classes are created using constructor patterns, prototype patterns, composite patterns, and so on. In ES6, classes can be created using the Class keyword.
Implement a singleton pattern
JS numeric types are divided into basic types (Undefined, Null, Boolean, Number, String,) and reference types (Object, Fucntion, Array). That is, base types are stored in stack memory, reference types are stored in heap memory, and Pointers to the contents of the heap are stored in the stack.
So the core idea of the singleton pattern in JS is: every time the constructor is called, return a pointer to the same object. That is, we only create a new object when the constructor is called the first time and then return it when the call returns. So the focus becomes how to cache variable objects that are created for the first time.
Of course, global variables are excluded first. Because global variables are easy to modify and pollute, some unexpected situations can occur. So you decided to use the following method to cache the created objects.
1. Constructor
/ / ES5 implementationfunctionPresident(name) {// If the instance already existsif (typeof President.onlyPresident === 'object') {
returnName = name // cache President. OnlyPresident = thisreturn this
}
var president1 = new President("Obama")
var president2 = new President("Trump"Console. log(president1) Console. log(president2) //ES6 implement class President {constructor(name) {console.log(president1) console.log(president2)if(! President.onlyPresident) { this.name = name President.onlyPresident = this }return President.onlyPresident
}
}
var president3 = new President("Obama")
var president4 = new President("Trump")
console.log(president3)
console.log(president4)
Copy the code
If you look at the two prints, each instance has a onlyPresident attribute in its constructor, which is the cached attribute of the first instantiation object.
Note: One disadvantage of this approach is that it exposes static attributes and can be artificially overridden.
Class President {constructor(name) {if(! President.onlyPresident) { this.name = name President.onlyPresident = this }return President.onlyPresident
}
}
var president3 = new President("Obama")
var president4 = new President("Trump")
president4.constructor.onlyPresident = George W. Bush// Change the static property console.log(president3) console.log(president4)Copy the code
2. Use closure functions
To use closures, you need to understand javascript’s special variable scope. There are only two types of scope for variables: global and local. The javascript language is unique in that global variables can be read directly from inside functions, but local variables cannot be read from outside functions. Note: When declaring variables inside functions, always use the var command. If not, you are actually declaring a global variable!
-
Definition of closure functions
The MDN interpretation is that the function and the references to its state (lexical environment) together constitute a closure. That is, closures allow you to access outer function scopes from inner functions.
A function A returns function B, and function B returns A variable in function A. A closure is A bridge between the inside of A function and the outside of A function.
Here is a simple closure example:
var Outer = function () { var countX = 0; return function () { console.log("This is the block-level scope of the closure.") return countX } } console.log(countX) //Uncaught ReferenceError: countX is not defined let x = Outer()() console.log(x) // 0 Copy the code
As you can see from the example above, the variable countX in the anonymous function is not externally accessible. But by nesting a function inside an anonymous function and returning it, you can receive the variable outside. This can act as a bridge between the inside and outside of the function. Also acts as a quarantine function scope to prevent variable contamination.
-
Implement a singleton based on the closure
function President(name) {
this.name = name
President.onlyPresident = this
President = function () {
return President.onlyPresident
}
}
President.prototype.getName=function(){
console.log(this.name)
}
var president1 = new President("Obama")
var president2 = new President("Trump")
var president3 = new President(George W. Bush)
console.log(president1) //President {name: "Obama"} president1.getName() // Obama console.log(president2) //President {} president2.getName() // Uncaught TypeError: president2.getName is not afunction
console.log(president3) //President {}
Copy the code
The idea behind the closure approach is that when the object is first created, the constructor is overridden and private variables are accessed inside the overridden constructor. There is a drawback to this, however: on the second time an instance is new, the previously added getName method is lost because the constructor has been overwritten, and an error is reported when president2.getName() is called. You can see the parameter reference in the debugger below:
console.log(president1.constructor === President) //false
console.log(president2.constructor === President) //true
Copy the code
- Optimize closure singletons
Now that we know where the above problems are, can we optimize the problem solving and the relationship between their prototype instances
function President(name) {
this.name = name
var instance = this
President = function () {
// President.prototype = this
returnPrototype = this.prototype = this.prototype = this.prototype = this.prototype = this.prototype = this Point directly to the prototype of the old President. The prototype = this. Constructor. The prototype instance = new President ()return instance
}
President.prototype.getName = function () {
console.log(this.name)
}
var president1 = new President("Obama")
var president2 = new President("Trump")
var president3 = new President(George W. Bush) president1.getName() // Obama 2.getName() // Obama 3.getName() // ObamaCopy the code
This is the form of the constructor you see in the debugger after changing the prototype chain in the first way. You can see that President’s prototype function contains the entire Presidnet constructor
3. Use immediate execution functions
A combination of closures and immediate functions is used to hold private variables. An immediate function is a method of calling a function that is executed immediately after it is declared. Such functions are usually called only once (which can be used on a singleton object) and destroyed immediately after the call. When used with closures, however, the referenced internal variables cannot be destroyed, so they are resident in memory. Verify the existence of instances during each access to prevent repeated instance creation.
Here is an example of a singleton that immediately executes a function and closure combination:
/ / the first stepfunction President(name) {
this.presidentName = name;
}
President.prototype.getName = function () {
returnthis.presidentName }; Var SinglePrisident = (function () {
var instance = null;
return function (name) {
if(! instance) { instance = new President(name); }returninstance; }}) (); Var prisident1 = SinglePrisident('Obama').getName()
var prisident2 = SinglePrisident('Trump').getName()
var prisident3 = SinglePrisident(George W. Bush// Step 4 console.log(prisident1) console.log(prisident2) console.log(prisident3)Copy the code
The code is divided into four steps for ease of description. When the first top-down execution of the code from step 1 to step 2, SinglePrisident, declares the function and allocates the memory space, it immediately executes the contents of the function, creates the function scope, and makes the variable manager the internal variable of the function. Take a look at the debugger as it executes to step 2.
As you can see, the immediate function already exists in Global at this point. It also includes a closure in the [[Scopes]]. The other parameters are undefined.
When the var prisident1 = onlyPrisident(‘ Obama ‘).getName() is null, a new Prisident instance is created, stored in a closure, and returned by return. Received by the variable prisident1.
But at the same time, the variable SinglePrisident has also been changed into A new function (temporarily called function A) which is the function inside the return, and the following is the new function of SinglePrisident
function (name) {
if(! instance) { instance = new President(name); }return instance;
}
Copy the code
Var prisident2 = SinglePrisident(‘ trump ‘).getName(); Instead, A new return function (function A above) is used to fetch the value of instance in the closure, where instance has been cached for the first time. So the second time still returns the object that was instantiated the first time.
‘Trump’
Of course, the result of the third run is the same as the second run, which is not described here. Interested partners can copy the code and check it out in the debugger.
conclusion
I was interested in the design pattern by accident recently. I wanted to have a deeper understanding of the strategy pattern. However, when I read the article, I was not familiar with the singleton pattern, so I have a deeper understanding of the cause and effect of THE JS singleton pattern, as well as the method and principle of creation. If it is helpful to you, please remember to give a thumbs up. If there is something wrong, please correct it and discuss and make progress together.