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 ~~