This is the 6th day of my participation in the August More Text Challenge
What is a singleton
As the name implies, a class has only one instance and can be accessed globally. For example, if a system has only one login popover, this login popover is suitable for singleton design.
How to implement
How do you know when you create an instance that this class has any previous instances?
1. flag
function A(name){
this.name=name
}
A.instance=null
A.getInstance=function(name){
if(!this.instance){
this.instance=new A(name)
}
return this.instance
}
Copy the code
2. The closure
Rewrite getInstance as follows:
A.getInstance=(function(name){
var instance=null
return function(name){
if(! instance){ instance=new A(nanme)
}
return instance
}
})()
Copy the code
Each time I want to create a singleton, I have to call getInstance. New doesn’t work.
3. Closure + namesake overlay
Modify the above example slightly, and you can directly new
var A=(function(){ // Note the use of var
var instance=null
var A=function(){
if(instance) return instance;
this.init()
return instance=this
}
A.prototype.init=function(){
// do something
}
})()
Copy the code
This approach is nice, but not very reusable, because A has two responsibilities: initialization and singleton judgment.
4. Use proxy classes
As the name suggests, a proxy class does the proxying, forwarding the primary responsibility. So let’s write A as A normal class.
var A=function(name){
this.name=name
this.init()
}
A.prototype.init=function(){
// do something
}
// Create the proxy class
var P=(function(){
var instance=null
return function(name){
if(! instance){ instance=new A(name)
}
return instance
}
})()
const p1=new P('aaa')
const p2=new P('bbb')
alert(p1===p2) // true
Copy the code
Application in JS
Because community update iteration speed is very fast, especially the front-end engineering in a variety of tools and frameworks and thought, let us at the time of development, for the variables of control is also a lot less, but we can’t just do a framework of users and just limited to the current operation, to know the framework for the realization of the function of some is also based on the underlying language, So it’s very necessary to understand these things.
As we all know, js is a class – free language, he has no concept of the “class”, introduced es6 class also just syntactic sugar, we use the thought of “class” in accordance with the above structure singleton, equivalent to take off pants break wind, we will do a singleton itself, just need a “only” objects, and can be accessed by global, And in JS to create an object is very easy, whether the object can be global access, of course, is the use of global variables, and global variables as JS a often criticized thing, how to deal with the least pollution?
1. Namespace
Using namespaces can greatly reduce global variables, and it is very simple to create.
const namespace={
a:function(){},
b: {c:() = >{}}}Copy the code
But if it is as an immutable this is far from enough, because even declared with const object types, also can be accidentally change, so I have to object to freeze, but difficult is that the object is to modify the original type and extension, but for the nested object types in the object attributes, can still be modified as follows:
const namespace={
a:function(){},
b: {c:() = >{}}}Object.freeze(namespace)
namespace.d='ddddd' // Cannot be extended
namespace.b='bbbbbb' // Attributes cannot be modified
namespace.b.c='cccccc' // However, nested objects can be modified with attributes and extensions
console.log(namespace)
ƒ () b: {c: "CCCCCC "} */
Copy the code
Property B has to be processed as follows:
Object.freeze(namespace)
Object.defineProperty(namespace.b,'c', {writable:false.configurable:false
})
namespace.d='ddddd' // Cannot be extended
namespace.b='bbbbbb' // Attributes cannot be modified
namespace.b.c='cccccc' // The embedded object cannot be modified
console.log(namespace)
/* A: ƒ () b: {c: ƒ} */
Copy the code
But the downside here is that I have to know that it has the properties of the object type and then encapsulate it, and if there are properties of the object in the properties of the object, then I have to do a layer upon layer of recursive implementation, which makes the head bigger.
2. Encapsulate private variables with closures
Encapsulate private variables inside closures, exposing only interfaces.
var namespace=(function(){
const _a=function(){}
const _b={
c:() = >{}}return {
getNameSpace:function(){
return ({
a:_a,
b:_b
})
}
}
})()
Object.freeze(namespace)
namespace.getNameSpace='1111'
console.log(namespace.getNameSpace())
/* Output: a: ƒ () b: {c: ƒ} */
Copy the code
Inert singleton
Lazy singletons are instances of objects that are created only when needed, such as the example we implemented in section 1:
function A(name){
this.name=name
}
A.instance=null
A.getInstance=function(name){
if(!this.instance){
this.instance=new A(name)
}
return this.instance
}
Copy the code
But this is based on the “class” design, the last section, in JS to write this singleton pattern is equivalent to pants fart, then what singleton pattern implementation is the most universal? I will introduce an example to analyze it.
In one system, we need a login button, and clicking the login button brings up the login popover.
There are two design ideas that can be implemented. One is to write the popover component and hide it, and then change the CSS to make it visible. The second is to create a popover when the user clicks the login button. Since this section deals with lazy singletons, the implementation of the former is out of scope, so let’s implement the latter.
const createModal=function(){
let modal=null
return function(){
if(! modal){ modal=document.createElement('div')
modal.innerHTML= `login modal`
modal.style.display='none'
document.body.appendChild(modal)
}
return modal
}
}()
document.getElementById('loginBtn').onclick=() = >{
const loginModal=createModal()
loginModal.style.display='block'
}
Copy the code
So login Modal is only created once when I hit the login button multiple times.
But the createModal function is not a single function. To make the singleton pattern useful in many ways, you need to extract the logic from this part of the function.
const getSingle=function(fn){
var res=null
return function(){
return res||(res= fn.apply(this.arguments))}}Copy the code
Then we implement that requirement:
const createLoginLayer=function(){
const modal=document.createElement('div')
modal.innerHTML= `login modal`
modal.style.display='none'
document.body.appendChild(modal)
return modal
}
document.getElementById('loginBtn').onclick=() = >{
const createSingleLoginModal=getSingle(createLoginLayer)
const loginModal=createSingleLoginModal()
loginModal.style.display='block'
}
Copy the code
If there are any additional singleton components that need to be constructed:
const createA=function(){
/ /...
}
const createSingleA=getSingle(createA)
createSingleA()
Copy the code
That’s the single principle
GetSingle () is not limited to dom creation, of course, but can do the same with functions such as binding events to elements. But!!! Don’t use this function just because it’s easy to use, because closures take up memory and are difficult to debug, so use them only when necessary.
Recently I have been reading JS design Patterns and development practices. This article is my summary and expansion of this book. Next article on strategy patterns ~~