Home >Web Front-end >JS Tutorial >Detailed explanation of Javascript modular programming_Basic knowledge

Detailed explanation of Javascript modular programming_Basic knowledge

WBOY
WBOYOriginal
2016-05-16 16:29:551452browse

Modular programming is a very common Javascript programming model. It generally makes code easier to understand, but there are many good practices that are not widely known.

Basics

Let’s start with a brief overview of some modular patterns since Eric Miraglia (the developer of YUI) first published a blog describing the modular pattern three years ago. If you are already very familiar with these modular modes, you can skip this section and start reading from the "Advanced Mode".

Anonymous closure

This is the basic structure that makes everything possible, and it is also the best feature of Javascript. We will simply create an anonymous function and execute it immediately. All code will run within this function and live in a closure that provides privatization, which is enough to make the variables in these closures available throughout the entire life cycle of our application.

Copy code The code is as follows:

(function () {
// ... all vars and functions are in this scope only
// still maintains access to all globals
}());

Note the pair of outermost parentheses surrounding the anonymous function. Because of the language characteristics of Javascript, this pair of parentheses is necessary. In JavaScript, statements starting with the keyword function are always considered function declarations. Wrapping this code in parentheses tells the interpreter that it is a function expression.

Global variable import

Javascript has a feature called implicit global variables. No matter where a variable name is used, the interpreter will follow the scope chain backwards to find the var declaration statement of the variable. If no var declaration is found, the variable is considered a global variable. If this variable is used in an assignment statement and the variable does not exist, a global variable will be created. This means it's easy to use or create global variables in anonymous closures. Unfortunately, this results in code that is extremely difficult to maintain, because to the human eye, it is impossible to tell which variables are global at a glance.

Luckily, our anonymous function provides an easy workaround. By simply passing global variables as arguments to our anonymous functions, we get cleaner and faster code than implicit global variables. Here is an example:

Copy code The code is as follows:

(function ($, YAHOO) {
// now have access to globals jQuery (as $) and YAHOO in this code
}(jQuery, YAHOO));

Module export

Sometimes you not only want to use global variables, you also want to declare them for repeated use. We can do this easily by exporting them - via the return value of the anonymous function. Doing this will complete a basic prototype of the modular pattern, followed by a complete example:

Copy code The code is as follows:

var MODULE = (function () {
var my = {},
        privateVariable = 1;
function privateMethod() {
               // ...
}
My.moduleProperty = 1;
My.moduleMethod = function () {
               // ...
};
Return my;
}());

Note that we have declared a global module called MODULE, which has two public properties: a method called MODULE.moduleMethod and a variable called MODULE.moduleProperty. In addition, it maintains a private built-in state using anonymous function closures. At the same time, we can easily import the global variables we need and use this modular pattern as we learned before.

Advanced Mode

The foundation described in the above section is sufficient for many situations, and now we can develop this modular pattern further to create more powerful and extensible structures. Let's start with the MODULE module and introduce these advanced modes one by one.

Magnification mode

It is a limitation of modular mode that the entire module must be in one file. Anyone who has worked on a large project will understand the value of splitting js into multiple files. Fortunately, we have a great implementation for amplifying modules. First, we import a module, add properties to it, and finally export it. Here's an example - zoom it in from the original MODULE:

Copy code The code is as follows:

var MODULE = (function (my) {
My.anotherMethod = function () {
// added method...
};
Return my;
}(MODULE));

We use the var keyword to ensure consistency, although it is not required here. After this code is executed, our module already has a new public method called MODULE.anotherMethod. The amplification file also maintains its own private built-in state and imported objects.

Wide zoom mode

Our above example requires our initialization module to be executed first, and then the amplification module can be executed. Of course, sometimes this may not necessarily be necessary. One of the best things a Javascript application can do to improve performance is execute scripts asynchronously. We can create flexible multi-part modules and enable them to be loaded in any order through permissive enlargement mode. Each file needs to be organized according to the following structure:

Copy code The code is as follows:

var MODULE = (function (my) {
// add capabilities...
Return my;
}(MODULE || {}));

In this pattern, the var expression is required. Note that if MODULE has not been initialized, this import statement will create MODULE. This means you can use a tool like LABjs to load all your module files in parallel without blocking.

Tight zoom mode

Wide zoom mode is great, but it also puts some limitations on your modules. Most importantly, you cannot safely override a module's properties. You also cannot use properties from other files during initialization (but you can use them at runtime). Tight amplification mode involves a sequential sequence of loads and allows overriding properties. Here's a simple example (zooming in on our original module):

Copy code The code is as follows:

var MODULE = (function (my) {
var old_moduleMethod = my.moduleMethod;
My.moduleMethod = function () {
// method override, has access to old through old_moduleMethod...
};
Return my;
}(MODULE));

We overrode the implementation of MODULE.moduleMethod in the above example, but can maintain a reference to the original method when needed.

Cloning and Inheritance

Copy code The code is as follows:

var MODULE_TWO = (function (old) {
var my = {},
         key;
for (key in old) {
If (old.hasOwnProperty(key)) {
                my[key] = old[key];
}
}
var super_moduleMethod = old.moduleMethod;
My.moduleMethod = function () {
// override method on the clone, access to super through super_moduleMethod
};
Return my;
}(MODULE));

This mode is probably the least flexible option. It does make the code look cleaner, but that comes at the cost of flexibility. As I wrote above, if a property is an object or function, it will not be copied, but will become a second reference to the object or function. Modifying one of them will modify the other at the same time (Translator's Note: Because they are basically the same!). This object cloning problem can be solved through the recursive cloning process, but function cloning may not be able to solve it. Maybe eval can be used to solve it. Therefore, I describe this method in this article only for the sake of completeness.

Cross-file private variables

There is a major limitation in splitting a module into multiple files: each file maintains its own private variables, and cannot access the private variables of other files. But this problem can be solved. Here is an example of a permissive module that maintains private variables across files:

Copy code The code is as follows:

var MODULE = (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;
        };
// permanent access to _private, _seal, and _unseal
Return my;
}(MODULE || {}));

All files can set attributes on their respective _private variables, and it is understood that they can be accessed by other files. Once this module is loaded, the application can call MODULE._seal() to prevent external calls to the internal _private. If the module needs to be rescaled, internal methods in either file can call _unseal() before loading the new file, and call _seal() again after the new file has been executed. I use this pattern at work today, and I haven't seen this approach elsewhere. I think this is a very useful pattern, and it's worth writing an article on the pattern itself.

Submodule

Our last advanced mode is by far the easiest. There are many excellent examples of creating submodules. This is just like creating a regular module:

Copy code The code is as follows:

MODULE.sub = (function () {
var my = {};
// ...
Return my;
}());

Although this may seem simple, I think it’s worth mentioning here. Submodules have all the advanced advantages of general modules, including amplification mode and privatization state.

Conclusion

Most advanced modes can be combined together to create a more useful mode. If I were to recommend a modular pattern for designing complex applications, it would be a combination of lenient enlargement patterns, private variables, and submodules.

I haven't thought about the performance issues of these patterns, but I'd rather translate this into a simpler way of thinking: if a modular pattern has good performance, then it can do a good job of minimizing, Makes downloading this script file faster. Using lenient amplification mode allows simple non-blocking parallel downloads, which results in faster downloads. Initialization time may be slightly slower than other methods, but the trade-off is worth it. As long as global variables are imported correctly, there should be no impact on runtime performance, and it's possible to get faster execution speeds by shortening reference chains with private variables in submodules.

To end, here is an example of a submodule dynamically loading itself into its parent module (creating the parent module if it does not exist). For the sake of simplicity, I removed the private variables. Of course, adding private variables is also very simple. This programming model allows an entire complex hierarchical code base to be loaded in parallel via submodules.

Copy code The code is as follows:

var UTIL = (function (parent, $) {
var my = parent.ajax = parent.ajax || {};
My.get = function (url, params, callback) {
// ok, so I'm cheating a bit :)
          return $.getJSON(url, params, callback);
};
// etc...
Return parent;
}(UTIL || {}, jQuery));

This article summarizes the current best practices of "Javascript modular programming" and explains how to put them into practice. Although this is not an introductory tutorial, you can understand it as long as you have a little understanding of the basic syntax of Javascript.

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn