Introduction
Module mode is a very common mode in JavaScript programming. Generally, everyone knows the basic usage. This article tries to give you more advanced usage of this mode.
First, let’s take a look at the basic features of the Module pattern:
Modular, reusable
Encapsulates variables and functions, has no contact with the global namaspace, and is loosely coupled
Only exposes available public methods. All other private methods are hidden
Regarding the Module mode, this concept was first proposed by Eric Miraglia, a member of YUI, 4 years ago. We will explain the basic usage with a simple example (if you are already very familiar with it, Please ignore this section).
Basic usage
Let’s take a look at the simplest implementation first. The code is as follows:
var Calculator = function (eq) {
//You can declare private members here
var eqCtl = document.getElementById(eq);
return {
// Exposed Public members
add: function (x, y) {
var val = x y;
eqCtl.innerHTML = val;
}
};
};
We can call it in the following way:
var calculator = new Calculator('eq');
calculator.add(2, 2);
You may have seen that it needs to be new every time it is used. That is to say, each instance is a copy in the memory. If you do not need to pass parameters or do not have some special stringent requirements, we can add a bracket after the last } to achieve the purpose of self-execution, so that the There will only be one copy of the instance in memory, but before showing its advantages, let's take a look at the basic usage of this mode.
Anonymous closure
Anonymous closure is the foundation that makes everything possible, and this is also the best feature of JavaScript. Let’s create the simplest closure function. The code inside the function always exists within the closure. , during the entire running cycle, this closure ensures that the internal code is in a private state.
(function () {
// .. . All variables and functions are declared here, and the scope can only be within this anonymous closure
// ...but the code here can still access external global objects
}());
Note that the parentheses after the anonymous function are required by the JavaScript language, because if you do not declare it, the JavaScript interpreter will declare a function by default. If there are parentheses, it will create a function expression. formula, that is, self-executing. When using it, you don’t need to use new as above. Of course, you can also declare it like this:
(function () {/* internal code*/})();
But we It is recommended to use the first method. Regarding function self-execution, I will have a special article to explain it in detail later, so I won’t go into details here.
Referring to global variables
JavaScript has a feature called implicit global variables. Regardless of whether a variable has been used or not, the JavaScript interpreter traverses the scope chain in reverse to find the var declaration of the entire variable. If var is not found, explain The interpreter assumes that the variable is a global variable. If the variable is used for an assignment operation, if it does not exist before, the interpreter will automatically create it. This means that it is very easy to use or create global variables in anonymous closures, but What is more difficult is that the code is difficult to manage, especially when the person reading the code sees a lot of distinctions between which variables are global and which are local.
However, fortunately, we can provide a relatively simple alternative in anonymous functions. We can pass global variables into anonymous functions as parameters and then use them. Compared with implicit global variables, it is clearer and faster. Let’s look at an example:
(function ($, YAHOO ) {
// Here, our code can use the global jQuery object, and the same goes for YAHOO
} (jQuery, YAHOO));
Nowadays, many class libraries There are such usage methods, such as jQuery source code.
However, sometimes you may not only want to use global variables, but also want to declare global variables. How to do this? We can return this global variable through the return value of the anonymous function, which is a basic Module mode. Let’s look at a complete code:
var blogModule = (function () {
var my = {}, privateName = "Blog Park";
function privateAddTopic(data) {
//Here is the internal processing code
}
my.Name = privateName;
my.AddTopic = function (data) {
privateAddTopic(data);
};
return my;
} ()) ;
The above code declares a global variable blogModule with 2 accessible properties: blogModule.AddTopic and blogModule.Name. In addition, other codes are in the anonymous function. Closures maintain private state. At the same time, according to the above example of passing in global variables, we can also easily pass in other global variables.
Advanced Usage
The above content is sufficient for most users, but we can also extend more powerful and easy-to-extend structures based on this pattern. Let's look at them one by one.
Extensions
One limitation of the Module mode is that all code must be written in one file, but in some large projects, it is very important to separate a function into multiple files, because it can be easily developed by multiple people. . Looking back at the global parameter import example above, can we pass the blogModule itself in? The answer is yes. We first pass the blogModule in, add a function attribute, and then return it to achieve the purpose we said. The above code:
var blogModule = (function (my) {
my.AddPhoto = function () {
//Add internal code
};
return my;
} (blogModule));
Does this code look like an extension method in C#? Somewhat similar, but essentially different. At the same time, although var is not necessary, we use it again to ensure consistency. After the code is executed, AddPhoto under blogModule can be used. At the same time, the code inside the anonymous function still ensures privacy and internal state.
Loosely coupled extension
Although the above code can be executed, the blogModule must be declared first and then the above extension code is executed. This means that the steps cannot be messed up. How to solve this problem? Let’s recall that we usually declare variables like this:
var cnblogs = cnblogs || {};
This is to ensure that the cnblogs object is used directly when it exists, and directly when it does not exist. Assigned to {}, let’s see how to use this feature to implement any loading order in Module mode:
var blogModule = (function (my) {
// Add some functions
return my;
} (blogModule || {}));
With this code, each separate file guarantees this structure, then we can achieve loading in any order. Therefore, var must be declared at this time, because if it is not declared, other files will read Can't get it.
Tightly coupled extensions
Although loosely coupled extensions are awesome, there may be some limitations. For example, you cannot rewrite some of your properties or functions, and you cannot use Module properties during initialization. . Tightly coupled extensions limit the loading order, but provide us with the opportunity to overload, see the following example:
var blogModule = (function (my) {
var oldAddPhotoMethod = my.AddPhoto;
my.AddPhoto = function () {
// Overloaded method, still available Call the old method through oldAddPhotoMethod
};
return my;
} (blogModule));
In this way, we achieve the purpose of overloading, of course if If you want to continue to use the original properties internally, you can call oldAddPhotoMethod to use it.
Clone and inheritance
var blogModule = (function ( old) {
var my = {},
key;
for (key in old) {
if (old.hasOwnProperty(key)) {
my[key] = old[ key];
}
}
var oldAddPhotoMethod = old.AddPhoto;
my.AddPhoto = function () {
// After cloning, it has been rewritten, and of course you can continue to call it oldAddPhotoMethod
};
return my;
} (blogModule));
This method is flexible, but it also requires flexibility. In fact, the attribute object or function of the object is not copied at all, it just has one more reference to the same object, so if the old object changes it , the attributes or functions owned by the cloned object will also be changed. To solve this problem, we have to use recursion, but recursion is not easy to use for function assignment, so we eval accordingly when recursing. function. No matter what, I still put this method in this post, so everyone can just pay attention when using it.
Share private objects across files
Through the above example, we know that if a module is split into multiple files, each file needs to ensure the same structure, that is to say, the private object in the anonymous function of each file Neither can cross-access, so what should we do if we have to use it? Let’s look at a piece of code first:
var blogModule = (function ( my) {
var _private = my._private = my._private || {},
_seal = my._seal = my._seal || function () {
delete my._private;
delete my._seal;
delete my._unseal;
},
_unseal = my._unseal = my._unseal || function () {
my._private = _private;
my. _seal = _seal;
my._unseal = _unseal;
};
return my;
} (blogModule || {}));
Any file will do Set attributes to their local variables _private, and the settings will take effect immediately on other files. Once this module is loaded, the application will call blogModule._seal() to "lock" it, which will prevent external access to the internal _private. If this module needs to be regenerated, any file during the application's life cycle can be "unlocked" by calling _unseal() and then load the new file. After loading, call _seal() again to "lock".
Submodule
The last and easiest way to use it is to create a submodule
blogModule.CommentSubModule = (function () {
var my = {};
// ...
return my;
} ());
Although it is very simple, I put it in because I want to explain that submodules also have all the advanced usage methods of general modules, which means that you can use any submodule again Some application methods above.
Summary
Most of the above methods can be used in combination with each other. Generally speaking, if you want to design a system, you may use methods such as loosely coupled extensions, private state, and submodules. In addition, I didn't mention the performance issue here, but I think the Module mode is efficient, has less code, and has fast loading speed. Using loose coupling extensions allows for parallel loading, which can improve download speeds. Initialization time may be slower, but it's worth it to use a good pattern.
Reference article:
http://yuiblog.com/blog/2007/06/12/module-pattern/
http://www.adequatelygood.com/2010/3/JavaScript-Module- Pattern-In-Depth
Synchronization and recommendation
This article has been synchronized to the directory index: In-depth understanding of JavaScript series
In-depth understanding of JavaScript series of articles, including original, translated, reprinted and other types of articles, if it is useful to you It is useful, please recommend and support it to give uncle the motivation to write.