As a software development model, monolithic pattern has been widely used in many object-oriented languages. In JavaScript, monolithic pattern is also widely used. However, because the JavaScript language has its unique object-oriented method, it is consistent with some traditional object-oriented languages in the idea of monolithic languages, but there are still differences in implementation.
First, let’s take a look at the definition of monomer patterns in traditional object-oriented languages: monomer patterns are classes that can only be instantiated once and can be accessed through a well-known access point. There are two points in this definition that highlight the characteristics of traditional object-oriented languages, namely class and instantiation. Therefore, for traditional object-oriented languages, the monolithic pattern is based on the natural characteristics of its class and instantiation. That is, use the keyword class to define a class, which can be instantiated through the new keyword, but it needs to be ensured that every time it is instantiated by new, the same instance is obtained or that its constructor can only be called once through new.
Let’s take a look at the definition of monomer pattern in JavaScript: monomer is an object used to divide the namespace and organize a batch of related methods and attributes together. If it can be instantiated, it can only be instantiated once. Comparing the above definition, you will find that the monomer definition here defines its essence as an object, rather than a class in a traditional object-oriented language, which also shows that the language of javascript is object-based. At the same time, it was pointed out later that if it can be instantiated, this shows that there should be several ways to define monomers in JavaScript. There are one or more ways to instantiate that can be instantiated, that is, use the new keyword to create monomer objects. However, this method is not a natural feature of JavaScript itself, because objects created using the new keyword are actually simulated and defined through functions (although ES6 has begun to support the class keyword, it has not yet been widely supported by the browser). So how to use the natural features of JavaScript to implement monomer patterns?
var Singleton={ attribute1:true, attribute2:10, method1:function(){ }, method2:function(arg){ }}Here is an object Singleton, which contains several properties and methods. It is included in the page. This object is created when js is loaded. It is called using Singleton.method1 when calling. Its instantiation is completed during the execution of the page loading js parsing. We do not use the new keyword to instantiate this object. This is also a big difference between implementing monolithic patterns in JavaScript and traditional object-oriented languages. This method is simpler and easier to understand. However, this method has several disadvantages. One obvious disadvantage is that it does not provide a namespace. If other programmers also define a Singleton variable in the page, it is easy to overwrite and confuse this monolithic object. So for this problem, it is rewritten as follows:
var mySpace={};mySpace.Singleton={ attribute1:true, attribute2:10, method1:function(){ }, method2:function(arg){ }}Here, a mySpace namespace is defined first, and then the single object Singleton is mounted below this object, which greatly reduces the possibility of conflicts with other programmers and misoperation. Even if others define a Singleton variable in the global scope, it will not pollute the singleton object. This implements the function of dividing the namespace and organizing some related attributes and methods as mentioned in the previous definition.
This method still has its disadvantages. All properties and methods of this monolithic object are shared and can be accessed and modified at any time from the outside. Therefore, closures are used to simulate private properties and methods, as follows:
mySpace.Singleton=(function(){ var privateAttribute1=false; var privateAttribute1=[1,2,3]; function privateMethod1(){ } function privateMethod2(){ } return { publicAttribute1:true, publicAttribute2:10, publicMethod1:function(){ privateAttribute1=true; privateMethod1(); }, publicMethod2:function(arg){ privateAttribute1=[4,5,6]; privateMethod2(); } }})();Here we directly assign an anonymous self-executing function to the monolith object. In this function, the var and function keywords are used to define its private properties and methods respectively. These cannot be directly accessed outside the function (outside the monolith object), because once the function is executed, the space of its internal scope will be recycled, which is why closures can be used to simulate private properties and methods. In this function (closure), an object is finally returned, which contains some public methods and properties, which can be called directly outside. At the same time, since these public methods are defined inside the function, they can call their private properties and methods. However, the outside world can only complete certain operations through the returned public methods and properties, and cannot directly call properties such as Singleton.privateMethod1. This makes the monolithic object not only isolated the outside world to directly access its private properties and methods, but also provides some common properties and methods to the outside world to complete certain operations.
This monolithic pattern constructed by self-execution of anonymous functions is widely used in many js libraries, but there is still a problem. If we do not need to use the object when loading the page, and the creation of the object is more expensive (such as a lot of calculations or multiple access to the dom tree and its properties, etc.), it is reasonable to create it when it is needed, rather than directly creating it with the parsing and execution of js. This concept is called lazy loading, so modify the above code as follows:
mySpace.Singleton=(function(){ var uniqueInstance; function constructor(){ var privateAttribute1=false; var privateAttribute1=[1,2,3]; function privateMethod1(){ } function privateMethod2(){ } return { publicAttribute1:true, publicAttribute2:10, publicMethod1:function(){ privateAttribute1=true; privateMethod1(); }, publicMethod2:function(arg){ privateAttribute1=[4,5,6]; privateMethod2(); } } } return { getInstance:function(){ if(!uniqueInstance){ uniqueInstance=constructor(); } return uniqueInstance; } } } })();Here, a private variable uniqueInstance is defined in the anonymous function as a handle to determine whether a monolithic object has been created. Then, all the attributes and methods defined on the monolithic object have been placed in a function called constructor. Only when the function is called can the monolithic object be created, otherwise it will not be created directly. Then, return an object, which contains a getInstance method, which is for external calls. When calling the method, first determine whether the monolithic object exists. If it exists, return it directly. Otherwise, call the constructor function to construct the monolithic object and return it. Finally, if we call a method of the monolithic object, we need to use mySpace.Singleton.getInstance().publicMethod1(). Here, we will only create this monolithic object when we call this way, otherwise the monolithic object will not be created automatically, which actually implements on-demand loading or lazy loading.