Home Web Front-end JS Tutorial Implementation method of cyclic loading of JavaScript modules_javascript skills

Implementation method of cyclic loading of JavaScript modules_javascript skills

May 16, 2016 pm 03:26 PM

"Circular dependency" means that the execution of script a depends on script b, and the execution of script b depends on script a.

// a.js
var b = require('b');

// b.js
var a = require('a');
Copy after login

Usually, "loop loading" indicates the existence of strong coupling. If not handled well, it may also lead to recursive loading, making the program unable to execute, so it should be avoided.

But in fact, this is difficult to avoid, especially for large projects with complex dependencies. It is easy for a to depend on b, b to c, and c to depend on a. This means that the module loading mechanism must take into account "loop loading" situations.

This article introduces how the JavaScript language handles "loop loading". Currently, the two most common module formats, CommonJS and ES6, have different processing methods and return different results.

1. Loading principle of CommonJS module

Before introducing how ES6 handles "loop loading", let's 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 will execute the entire script and then generate an object in memory.

{
 id: '...',
 exports: { ... },
 loaded: true,
 ...
}
Copy after login

In the above code, the id attribute of the 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 the module has been executed. There are many other attributes, but they are omitted here. (For detailed introduction, please refer to "require() Source Code Interpretation".)

When you need to use this module in the future, you will get the value from the exports attribute. Even if the require command is executed again, the module will not be executed again, but the value will be retrieved from the cache.

2. Loop loading of CommonJS module

An important feature of the CommonJS module is execution when loading, that is, all script code will be executed when required. CommonJS's approach is that once a module is "loop loaded", only the executed part will be output, and the unexecuted part will not be output.

Let’s take a look at the examples in the official documentation. The code of the script file a.js is as follows.

exports.done = false;
var b = require('./b.js');
console.log('在 a.js 之中,b.done = %j', b.done);
exports.done = true;
console.log('a.js 执行完毕');
Copy after login

In the above code, the a.js script first outputs a done variable, and then loads another script file b.js. Note that the a.js code stops here at this time, waiting for b.js to complete execution, and then continues execution.

Look at the b.js code again.

exports.done = false;
var a = require('./a.js');
console.log('在 b.js 之中,a.done = %j', a.done);
exports.done = true;
console.log('b.js 执行完毕');
Copy after login

In the above code, when b.js is executed to the second line, a.js will be loaded. At this time, "loop loading" occurs. The system will get the value of the exports attribute of the object corresponding to the a.js module. However, because a.js has not yet been executed, only the executed part can be retrieved from the exports attribute, not the final value.

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


exports.done = false;


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

Then, b.js continues to execute. When all executions are completed, the execution right is returned to a.js. Therefore, a.js continues to execute until the execution is completed. We write a script main.js to verify this process.

var a = require('./a.js');
var b = require('./b.js');
console.log('在 main.js 之中, a.done=%j, b.done=%j', a.done, b.done);
Copy after login

Execute main.js and the results are as follows.

$ node main.js

在 b.js 之中,a.done = false
b.js 执行完毕
在 a.js 之中,b.done = true
a.js 执行完毕
在 main.js 之中, a.done=true, b.done=true
Copy after login

The above code proves two things. First, in b.js, a.js has not been executed, only the first line has been executed. Second, when main.js is executed to the second line, b.js will not be executed again, but the cached execution result of b.js will be output, that is, its fourth line.

exports.done = true;

3. Loop loading of ES6 modules

The operating mechanism of ES6 modules is different from CommonJS. When it encounters the module loading command import, it will not execute the module, but only generate a reference. Wait until you really need to use it, then get the value in the module.

Therefore, ES6 modules are dynamic references, there is no problem of caching values, and the variables in the module are bound to the module in which they are located. Please see the example below.

// 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);
Copy after login

In the above code, the variable foo of m1.js is equal to bar when it is first loaded. After 500 milliseconds, it becomes equal to baz again.

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

$ babel-node m2.js

bar
baz
Copy after login

The above code shows that the ES6 module does not cache the running results, but dynamically obtains the value of the loaded module, and the variable is always bound to the module in which it is located.

This causes ES6 to handle "loop loading" essentially differently from CommonJS. ES6 doesn't care at all whether "loop loading" occurs, it just generates a reference to the loaded module. The developer needs to ensure that the value can be obtained when the value is actually obtained.

Please see the following example (excerpted from "Exploring ES6" by Dr. Axel Rauschmayer).

// a.js
import {bar} from './b.js';
export function foo() {
 bar(); 
 console.log('执行完毕');
}
foo();

// b.js
import {foo} from './a.js';
export function bar() { 
 if (Math.random() > 0.5) {
 foo();
 }
}
Copy after login

按照CommonJS规范,上面的代码是没法执行的。a先加载b,然后b又加载a,这时a还没有任何执行结果,所以输出结果为null,即对于b.js来说,变量foo的值等于null,后面的foo()就会报错。

但是,ES6可以执行上面的代码。

$ babel-node a.js

执行完毕

a.js之所以能够执行,原因就在于ES6加载的变量,都是动态引用其所在的模块。只要引用是存在的,代码就能执行。

我们再来看ES6模块加载器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);
}
Copy after login

上面代码中,even.js里面的函数foo有一个参数n,只要不等于0,就会减去1,传入加载的odd()。odd.js也会做类似操作。

运行上面这段代码,结果如下。

$ babel-node
> import * as m from './even.js';
> m.even(10);
true
> m.counter
6
> m.even(20)
true
> m.counter
17
Copy after login

上面代码中,参数n从10变为0的过程中,foo()一共会执行6次,所以变量counter等于6。第二次调用even()时,参数n从20变为0,foo()一共会执行11次,加上前面的6次,所以变量counter等于17。

这个例子要是改写成CommonJS,就根本无法执行,会报错。

// 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);
}
Copy after login

上面代码中,even.js加载odd.js,而odd.js又去加载even.js,形成"循环加载"。这时,执行引擎就会输出even.js已经执行的部分(不存在任何结果),所以在odd.js之中,变量even等于null,等到后面调用even(n-1)就会报错。

$ node
> var m = require('./even');
> m.even(10)
TypeError: even is not a function
Copy after login

[说明] 本文是我写的《ECMAScript 6入门》第20章《Module》中的一节。

Statement of this Website
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

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

AI Hentai Generator

AI Hentai Generator

Generate AI Hentai for free.

Hot Article

R.E.P.O. Energy Crystals Explained and What They Do (Yellow Crystal)
1 months ago By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Best Graphic Settings
1 months ago By 尊渡假赌尊渡假赌尊渡假赌
Will R.E.P.O. Have Crossplay?
1 months ago By 尊渡假赌尊渡假赌尊渡假赌

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

How do I create and publish my own JavaScript libraries? How do I create and publish my own JavaScript libraries? Mar 18, 2025 pm 03:12 PM

Article discusses creating, publishing, and maintaining JavaScript libraries, focusing on planning, development, testing, documentation, and promotion strategies.

How do I optimize JavaScript code for performance in the browser? How do I optimize JavaScript code for performance in the browser? Mar 18, 2025 pm 03:14 PM

The article discusses strategies for optimizing JavaScript performance in browsers, focusing on reducing execution time and minimizing impact on page load speed.

What should I do if I encounter garbled code printing for front-end thermal paper receipts? What should I do if I encounter garbled code printing for front-end thermal paper receipts? Apr 04, 2025 pm 02:42 PM

Frequently Asked Questions and Solutions for Front-end Thermal Paper Ticket Printing In Front-end Development, Ticket Printing is a common requirement. However, many developers are implementing...

Who gets paid more Python or JavaScript? Who gets paid more Python or JavaScript? Apr 04, 2025 am 12:09 AM

There is no absolute salary for Python and JavaScript developers, depending on skills and industry needs. 1. Python may be paid more in data science and machine learning. 2. JavaScript has great demand in front-end and full-stack development, and its salary is also considerable. 3. Influencing factors include experience, geographical location, company size and specific skills.

How do I debug JavaScript code effectively using browser developer tools? How do I debug JavaScript code effectively using browser developer tools? Mar 18, 2025 pm 03:16 PM

The article discusses effective JavaScript debugging using browser developer tools, focusing on setting breakpoints, using the console, and analyzing performance.

How to merge array elements with the same ID into one object using JavaScript? How to merge array elements with the same ID into one object using JavaScript? Apr 04, 2025 pm 05:09 PM

How to merge array elements with the same ID into one object in JavaScript? When processing data, we often encounter the need to have the same ID...

How do I use source maps to debug minified JavaScript code? How do I use source maps to debug minified JavaScript code? Mar 18, 2025 pm 03:17 PM

The article explains how to use source maps to debug minified JavaScript by mapping it back to the original code. It discusses enabling source maps, setting breakpoints, and using tools like Chrome DevTools and Webpack.

Demystifying JavaScript: What It Does and Why It Matters Demystifying JavaScript: What It Does and Why It Matters Apr 09, 2025 am 12:07 AM

JavaScript is the cornerstone of modern web development, and its main functions include event-driven programming, dynamic content generation and asynchronous programming. 1) Event-driven programming allows web pages to change dynamically according to user operations. 2) Dynamic content generation allows page content to be adjusted according to conditions. 3) Asynchronous programming ensures that the user interface is not blocked. JavaScript is widely used in web interaction, single-page application and server-side development, greatly improving the flexibility of user experience and cross-platform development.

See all articles