“This is the 8th day of my participation in the August More Text Challenge. For details, see: August More Text Challenge.”

preface

At the top end of the interview, often encounter some questions about design patterns, each time the answer is not ideal. Coincides with the more challenging activities in August, I am prepared to spend a month to get a good understanding of design patterns, so as to increase my confidence for the interview.

In the previous installment, I explained what singletons are and how to implement them. For more details, see “Interview Confidence” – Singletons of Design Patterns | August for more challenges.

This installment will focus on singletons in JavaScript. Why? Recall the definition of a singleton pattern that guarantees only one instance of a class and provides a global point of access to it.

Note the phrase: “Ensure that there is only one instance of a class.” An instance is an object. Objects are created from “classes”. This is a natural thing to do in a class-centric language. In Java, for example, if you want an object, you must first define a class, and the object is always created from the class. When you create an object in JavaScript, you don’t need a class to create it at all, so the traditional singleton implementation doesn’t work in JavaScript. So this article is devoted to the singleton pattern in JavaScript.

Global variables are not singletons

Use var to create a global variable object a. Object A is unique in the program and can be accessed globally. The singleton pattern is defined as if creating a global variable is a singleton pattern. However, global variables can be modified, whereas for classes created using singletons, objects generated by class instantiations cannot be modified after the first generation. So global variables are not singletons.

Implement a singleton pattern without using a class

For example, if you click on a login button and a login box pops up, how does that work?

The first implementation is to create the login box when the page is loaded. The login box is initially hidden and will be displayed when the user clicks the login button:

< HTML > <body> <button id="loginBtn"> Login </button> </body> <script> var loginModal = (function() {const element = document.createElement('div'); Element. InnerHTML = 'I'm logged in to the float window '; element.style.display = 'none'; document.body.appendChild(element); return element; }) (); document.getElementById('loginBtn').onclick = function() { loginModal.style.display = 'block'; }; </script> </html>Copy the code

One obvious problem with this implementation is that it lengthening the first page load time. The other way to do it is to create the login box when the user clicks the login button.

<! DOCTYPE HTML > < HTML > <body> <button id="loginBtn"> </button> </body> <script> const creatLoginModal = function() { const element = document.createElement('div'); Element. InnerHTML = 'I'm logged in to the float window '; element.style.display = 'none'; document.body.appendChild(element); return element; }; document.getElementById('loginBtn').onclick = function() { const loginModal = creatLoginModal(); loginModal.style.display = 'block'; }; </script> </html>Copy the code

It is now possible to create a login box and display it when the user clicks the login button, but a more serious problem has been discovered that the login box is being created repeatedly.

For the above problem, you can use the singleton pattern to solve it.

< HTML > <body> <button id="loginBtn"> </button> </body> <script> const creatLoginModal = (function() {let element; return function() { if (! element) { element = document.createElement('div'); Element. InnerHTML = 'I'm logged in to the float window '; element.style.display = 'none'; document.body.appendChild(element); } return element; }}) (); document.getElementById('loginBtn').onclick = function() { const loginModal = creatLoginModal(); loginModal.style.display = 'block'; }; </script> </html>Copy the code

In the above code, change the creatLoginModal method to a self-executing anonymous function to form a closure, create a variable element in the closure, which will be cached in the closure, and record the DOM of the last time the creatLoginModal method was called to create the login box. The element variable is then used to determine whether the login box has been created.

At this point you can see that you can use the singleton pattern without classes in JavaScript. It can be argued that there is no necessary connection between singletons and classes in JavaScript.

Inert singleton

The pattern of singletons implemented above can be called lazy singletons, where objects or instance objects are created only when needed. Lazy singletons are the focus of the singleton pattern, a technique that can be very useful in practical development. Let’s wrap a method to generate an lazy singleton.

const getSingle = function( fn ){ const result; return function(){ return result || ( result = fn .apply(this, arguments ) ); }};Copy the code

Extract from the creatLoginModal method the invariant part of the logic that implements the singleton (to determine whether the object has been generated). The result is generated by the function fn that creates the object or instance object, and the function fn is variable, so we pass the function fn as an argument.

Apply lazy singletons

Use the getSingle method encapsulated above to create lazy singletons.

For example, to bind the click event only once to an element that will be rendered repeatedly:

const bindEvent = getSingle(function(){ document.getElementById( 'div' ).onclick = function(){ alert ( 'click' ); } return true; }); Const render = function(){console.log(' start rendering '); bindEvent(); };Copy the code

For example, create a unique iframe for dynamically loading third-party pages:

const createSingleIframe = getSingle(function() {
   const iframe = document.createElement('iframe');
   document.body.appendChild(iframe);
   return iframe;
});
document.getElementById('btn').onclick = function() {
   const iframeLayer = createSingleIframe();
   iframeLayer.src = 'http://baidu.com';
};
Copy the code