Thoughts on the method and module loading technology of JavaScript cyclic loading module

Source: Internet
Author: User

Circular dependency indicates that the execution of script a depends on Script B, and the execution of Script B depends on script.


// A. js
Var B = require ('B ');

// B. js
Var a = require ('A ');

In general, "Loop loading" indicates strong coupling. If the processing is not good, recursive loading may occur, making the program unable to be executed. Therefore, avoid this problem.

But in fact, this is very difficult to avoid, especially for large projects with complex dependencies, it is easy to see a dependent on B, B dependent on c, and c dependent on. This means that the module loading mechanism must consider "loop loading.

This article describes how to deal with "loop loading" in JavaScript ". Currently, the two most common module formats are CommonJS and ES6. The processing methods are different and the returned results are different.

I. Loading principle of CommonJS module

Before introducing how ES6 handles "loop loading", we will first introduce the loading principle of the most popular CommonJS module format.

A module of CommonJS is a script file. The first time the require command loads the script, it executes the entire script and generates an object in the memory.


    {
Id :'...',
Exports :{...},
Loaded: true,
...
    }

In the code above, the id attribute of this object is the module name, the exports attribute is each interface output by the module, and the loaded attribute is a Boolean value, indicating whether the script of this module has been executed. There are many other attributes, which are omitted here.

When you need to use this module in the future, it will take the value above the exports attribute. Even if you run the require command again, the module is not executed again, but the value is in the cache.

II. Loop loading of CommonJS module

The important feature of the CommonJS module is to execute the code during loading, that is, all the script code will be executed during require. In CommonJS, once a module is "cyclically loaded", only the executed part is output, and the unexecuted part is not output.

Let's take a look at the examples in the official document. The code for script file a. js is as follows.


Exports. done = false;
Var B = require ('./B. Js ');
Console. log ('in a. js, B. done = % J', B. done );
Exports. done = true;
Console. log ('A. js execution completed ');

In the above code, the. js script first outputs a done variable, and then loads another script file B. js. Note: The a. js code will be stopped here. Wait until the execution of B. js is complete and then proceed.

Let's look at the B. js code.


Exports. done = false;
Var a = require ('./a. Js ');
Console. log ('In B. js, a. done = % J', a. done );
Exports. done = true;
Console. log ('B. js execution completed ');

In the above code, when B. js is executed to the second line, it will load a. js. At this time, "Loop loading" occurs ". The system will go to the exports attribute value of the object corresponding to the. js module. However, because a. js has not been executed, only the executed part can be retrieved from the exports attribute, rather than the final value.

The executed part of a. js has only one line.


Exports. done = false;

Therefore, for B. js, it inputs only one variable done from a. js and the value is false.

Then, execute B. js, wait until all execution is completed, and then return the execution right to a. js. As a result, a. js proceeds to the next step until the execution is complete. Let's write a script main. js to verify this process.


Var a = require ('./a. Js ');
Var B = require ('./B. Js ');
Console. log ('In main. js, a. done = % j, B. done = % J', a. done, B. done );

Run main. js and the running result is as follows.


$ Node main. js

In B. js, a. done = false
B. js execution completed
In a. js, B. done = true
A. js execution is complete
In main. js, a. done = true, B. done = true

The code above demonstrates two things. First, in B. js, a. js is not fully executed and only executes the first line. Second, when main. js is executed to the second line, it does not execute B. js again, but outputs the execution result of the cached B. js, that is, its fourth line.


Exports. done = true;

III. Loop loading of ES6 module

The operating mechanism of the ES6 module is different from that of CommonJS. When the module loads the command import, it does not execute the module, but generates only one reference. When it is necessary, go to the module to take the value.

Therefore, the ES6 module is a dynamic reference, and there is no cache value problem, and the variables in the module are bound to the module where it is located. See the following example.


// M1.js
Export var foo = 'bar ';
SetTimeout () => foo = 'Baz', 500 );

// M2.js
Import {foo} from './m1.js ';
Console. log (foo );
SetTimeout () => console. log (foo), 500 );

In the code above, the variable foo of m1.js is equal to bar at the time of loading. After 500 milliseconds, it becomes equal to baz.

Let's see if m2.js can correctly read this change.


$ Babel-node m2.js

Bar
Baz

The code above shows that the ES6 module does not cache the running results, but dynamically loads the module values, and variables are always bound to the module where they are located.

As a result, ES6 processes "loop loading" in essence different from CommonJS. ES6 does not care about whether "loop loading" occurs. It only generates a reference pointing to the loaded module and requires the developer to ensure that the value can be obtained when the value is true.

See the following example (from Discovery ES6 by Dr. Axel Rauschmayer).


// A. js
Import {bar} from './B. Js ';
Export function foo (){
Bar ();
Console. log ('execution completed ');
    }
Foo ();

// B. js
Import {foo} from './a. Js ';
Export function bar (){
If (Math. random () & gt; 0.5 ){
Foo ();
      }
    }

According to CommonJS specifications, the above code cannot be executed. A loads B first, and B loads a Again. At this time, a does not have any execution results, so the output result is null, that is, for B. for js, if the value of the variable foo is equal to null, the following foo () will report an error.

However, ES6 can execute the above code.


$ Babel-node a. js

Execution completed

The reason why a. js can be executed is that all the variables loaded by ES6 are dynamically referencing their modules. The code can be executed as long as the reference exists.

Let's take a look at an example provided by the ES6 module loader SystemJS.


// Even. js
Import {odd} from './odd'
Export var counter = 0;
Export function even (n ){
Counter ++;
Return n = 0 | odd (n-1 );
    }

// Odd. js
Import {even} from './even ';
Export function odd (n ){
Return n! = 0 & even (n-1 );
    }

In the above code, the function foo in even. js has a parameter n. As long as it is not equal to 0, 1 is subtracted and the loaded odd () is passed in (). Odd. js also performs similar operations.

Run the above code and the result is as follows.


$ Babel-node
> Import * as m from './even. Js ';
> M. even (10 );
True
> M. counter
    6
> M. even (20)
True
> M. counter
17

In the code above, when the parameter n is changed from 10 to 0, foo () will be executed six times in total, so the variable counter is equal to 6. When even () is called for the second time, the parameter n is changed from 20 to 0, and foo () is executed 11 times in total, plus the first 6 times, so the counter variable is equal to 17.

If this example is rewritten to CommonJS, it cannot be executed and an error is reported.


// Even. js
Var odd = require ('./odd ');
Var counter = 0;
Exports. counter = counter;
Exports. even = function (n ){
Counter ++;
Return n = 0 | odd (n-1 );
    }

// Odd. js
Var even = require ('./even'). even;
Module. exports = function (n ){
Return n! = 0 & even (n-1 );
    }

In the above code, even. js loads odd. js, while odd. js loads even. js again to form "loop loading ". The execution engine outputs the even. js has been executed (no results exist), so in odd. in js, the variable even is equal to null. When even (n-1) is called later, an error is returned.


$ Node
> Var m = require ('./even ');
> M. even (10)
TypeError: even is not a function

    

Thoughts on javascript module loading technology

Not long ago, a netizen asked me the question of using requireJs and seajs on the front end. I asked him if your company had prepared your own javascript library or javascript framework, his answer is nothing. He just heard that requirejs and seajs are new technologies that are very valuable and he wants to use them.

This netizen's problem caused me to think about the javascript module loading technology. In the previous article, I gave myself the basic structure of a javascript library, in fact, one of the reasons for writing this article is that I want to use technologies such as requirejs or seajs to re-design the basic model of my javascript library. When I have a deep understanding of this technology, I found that it is incorrect to use the module loading system to solve the problem of decoupling general code from business code in the javascript library, the function of the module loading system is to solve the dependencies between different javascript libraries, rather than helping you develop a javascript library.

So what is a javascript module loading system?

The module system mainly aims to solve the naming conflicts between operating objects in different javascript libraries and the dependencies between different javascript libraries. The module loading system is for large-scale web front-end applications or giant web front-end applications.

Generally, in a giant web front-end application page, the page has rich functions and complex services. As time passes, the page functions often change, as a result, front-end developers often need to develop functional modules for new functions. However, in the actual business, the functions of each functional module may also penetrate into each other and depend on each other. The relationship is complex, when pages are complex, the relationship between the front-end databases becomes difficult to manage and control. In this case, the module loading system will come in handy.

For most programmers, there are not many opportunities to independently undertake such a large web front-end application, but there are many opportunities to develop small and medium-sized web front-end applications, such as enterprise-level web projects, there are few types of javascript libraries used in such projects, and the dependencies between libraries are well controlled. It is not necessary to introduce any module management system. Even if many small and medium internet companies have webpages, it is estimated that it is not as complicated as the front-end of enterprise web applications, so the relationship between modules or javascript libraries is well managed. In fact, these small and medium-sized applications are aimed at some or a specific scenario. Therefore, I personally think that we can finally form an independent javascript library for such a web front-end project, the features of this library should be similar to those of jQuery: the mode of adding several plug-in libraries to a master database is designed to solve the problem of universality, it can be reused and migrated, and the purpose of the plug-in library is often related to the business code. However, to distinguish the scope of the main library and plug-in library, so I added the namespace feature to the library.

The Javascript module loading technology shares some similarities with hadoop technology, that is, they are all aimed at super large systems, and they can play their role only under certain conditions, these technologies are all introduced from large Internet companies, because the problems that large Internet companies must solve when their applications become more complex. When your system is still in its infancy, these technologies are often used with caution. We should find the simplest and most effective way to solve our actual problems. If you think this system will become larger and larger in the future, you should keep the interfaces that will use these technologies later. If you use them too early, it is very likely that when the system scale is expanded, the cost of code refactoring will be higher.

For a module loading system, the most suitable scenario is to solve the decoupling problem between large web front-end application modules. If we need to write a new javascript file, we will immediately use the module loading technology, this is not a suspicion of misuse of technology. Before using a technology, we should not only consider how it is used, how it is used, but also whether it has any value.

Finally, I want to talk about it. I think that small and medium-sized web front-end applications are deployed in production. Because javascript is not the most complex, it is best to pack all external javascript files into a javascript external file, the advantage is that the number of http requests is reduced. Using the module loading technology will make it very difficult to package files and even fail to do so (for example, the modules of requirejs and seajs are all file-based, each module is an independent file), which is contrary to the purpose of reducing http.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.