Home  >  Article  >  Web Front-end  >  In-depth analysis of JavaScript’s Module pattern programming

In-depth analysis of JavaScript’s Module pattern programming

coldplay.xixi
coldplay.xixiforward
2020-07-29 17:32:282931browse

In-depth analysis of JavaScript’s Module pattern programming

Basic knowledge

First we need to have an overview of the Module pattern (2007 by Eric Miraglia of YUI in the blog proposed), if you are already familiar with Module mode, you can skip this section and read "Advanced Mode" directly.

Related learning recommendations: javascript video tutorial

Anonymous function closure

Anonymous function closure Packages are the best feature of JavaScript, bar none, they make everything possible. Now let's create an anonymous function and execute it immediately. All code in the function is executed in a closure, and the closure determines the privacy and status of these codes during the entire execution process.

The code is as follows:

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

Note the brackets outside the anonymous function. This is due to the fact that statements starting with function in JavaScript are generally considered function declarations. Adding outer parentheses creates a function expression.

Global import

JavaScript has a feature called hidden global variables. When a variable name is used, the compiler will look up the statement using var to declare the variable. If not found the variable is considered global. If used like this during assignment, a global scope will be created. This means that it is very easy to create a global variable in an anonymous closure. Unfortunately, this can lead to unmanageable code because it is unclear to the programmer if global variables are not declared in a file. Fortunately, anonymous functions give us another option. We can import global variables into our code through the parameters of anonymous functions, which is faster and neater.

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 don’t want to use global variables, but you want to declare them. We can easily export them through the return value of anonymous functions. So much about the basic content of the Module pattern, here is a more complicated example.

The code is as follows:

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

Here we declare 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 private internal state through the closure of anonymous functions. Of course, we can also use the previously mentioned mode to easily import the required global variables

Advanced mode

What I mentioned earlier is sufficient for many needs, but we can delve deeper into this pattern to create some powerful scalable structures. Let's continue to learn through this module called MODULE, bit by bit.

Expansion

Currently, one limitation of the module mode is that the entire module must be written in one file. Everyone who has done large-scale code development knows the importance of separating a file into multiple files. Fortunately we have a great way to extend modules. First we import a module, then add attributes, and finally export it. The example here is to extend MODULE using the method mentioned above.

The code is as follows:

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

Although unnecessary, for the sake of consistency, we use the var keyword again. Then the code is executed, and the module will add a public method called MODULE.anotherMethod. The extension file also maintains its own private internal state and imports.

Loose Expansion

Our above example requires us to create the module first and then expand the module. This is not necessary. Loading scripts asynchronously is one of the best ways to improve the performance of your Javascript applications. . Through loose expansion, we create flexible modules that can be loaded in any order and divided into multiple files. The structure of each file is roughly as follows

The code is as follows:

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

In this mode, the var statement is required. If the imported module does not exist, it will be created. This means you can use tools like LABjs to load these module files in parallel.

Tight expansion

Although loose expansion is great, it also adds some limitations to your module. The most important point is that you have no way to safely override module attributes, and you cannot use module attributes in other files during initialization (but you can use them during runtime after initialization). Compact expansion includes a certain loading order, but supports rewriting. Here is an example (extending our original MODULE).

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));

Here we have rewritten MODULE.moduleMethod and retained the reference to the original method as required.

Copy and inheritance

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));

这种模式可能是最不灵活的选择。虽然它支持了一些优雅的合并,但是代价是牺牲了灵巧性。在我们写的代码中,那些类型是对象或者函数的属性不会被复制,只会以一个对象的两份引用的形式存在。一个改变,另外一个也改变。对于对象来说[g5] ,我们可以通过一个递归的克隆操作来解决,但是对于函数是没有办法的,除了eval。然而,为了完整性我还是包含了它。

跨文件的私有状态

把一个module分成多个文件有一很大的局限,就是每一个文件都在维持自身的私有状态,而且没有办法来获得其他文件的私有状态。这个是可以解决的,下面这个松拓展的例子,可以在不同文件中维持私有状态。

代码如下:

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 || {}));

每一个文件可以为它的私有变量_private设置属性,其他文件可以立即调用。当module加载完毕,程序会调用MODULE._seal(),让外部没有办法接触到内部的 _.private。如果之后module要再次拓展,某一个属性要改变。在载入新文件前,每一个文件都可以调用_.unsea(),,在代码执行之后再调用_.seal。

这个模式在我今天的工作中想到的,我从没有在其他地方见到过。但是我认为这是一个很有用的模式,值得单独写出来。

Sub-modules

最后一个高级模式实际上是最简单的,有很多创建子module的例子,就像创建一般的module一样的。

代码如下:

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

虽然这可能是很简单的,但是我决定这值得被写进来。子module有一般的module所有优质的特性,包括拓展和私有状态。

总结

大多数高级模式都可以互相组合来创建更有用的新模式。如果一定要让我提出一个设计复杂应用的方法的话,我会结合松拓展,私有状态,和子module。

在这里我没有提到性能相关的事情,但是我可以说,module模式对于性能的提升有好处。它可以减少代码量,这就使得代码的载入更迅速。松拓展使得并行加载成为可能,这同样提升的载入速度。初始化的时间可能比其他的方法时间长,但是这多花的时间是值得的。只要全局变量被正确导入了运行的时候就不会出问题,在子module中由于对变量的引用链变短了可能也会提升速度。

最后,这是一个子module自身动态加载的例子(如果不存在就创建),为了简介我没有考虑内部状态,但是即便考虑它也很简单。这个模式可以让复杂,多层次的代码并行的加载,包括子module和其他所有的东西。

代码如下:

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));

我希望这些内容是有用的,请在下面留言来分享你的想法。少年们,努力吧,写出更好的,更模块化的JavaScript。

The above is the detailed content of In-depth analysis of JavaScript’s Module pattern programming. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:jb51.net. If there is any infringement, please contact admin@php.cn delete