Implementation of JavaScript module-javascript tips-js tutorial

Source: Internet
Author: User
This article describes how to deal with the JavaScript Language & quot; loop loading & quot ;. Currently, the two most common module formats are CommonJS and ES6. The processing method is different, and the returned results are different. "loop loading" (circular dependency) indicates that, the execution of script a depends on script B, and the execution of script B depends on script.

// a.jsvar b = require('b');// b.jsvar 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. (For details, see require () source code interpretation.)

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 ('at. in 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 ('. /. js'); console. log ('in B. in js,. done = % j ',. 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 ('. /. js '); var B = require ('. /B. js'); console. log ('in main. in js,. done = % j, B. done = % j ',. done, B. done );

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

$ Node main. js in B. in js,. done = falseb. js execution is completed at. in js, B. done = truea. after js execution is completed. in js,. 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.jsexport var foo = 'bar';setTimeout(() => foo = 'baz', 500);// m2.jsimport {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.jsbarbaz

The Code indicates that the ES6 module does not cache the running results, but dynamically loads the module values and variables are always bound to the module.

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

//. Jsimport {bar} from '. /B. js'; export function foo () {bar (); console. log ('execution completed ');} foo (); // B. jsimport {foo} from '. /. js'; export function bar () {if (Math. random ()> 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.jsimport { odd } from './odd'export var counter = 0;export function even(n) { counter++; return n == 0 || odd(n - 1);}// odd.jsimport { 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.counter6> m.even(20)true> m.counter17

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.jsvar odd = require('./odd');var counter = 0;exports.counter = counter;exports.even = function(n) { counter++; return n == 0 || odd(n - 1);}// odd.jsvar 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

[Note] This article is a section in Chapter 20th "Module" in "ECMAScript 6 getting started.

Related Article

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.