Skip to content
标签
blog
reference
字数
974 字
阅读时间
4 分钟

CommonJS 模块的循环加载 ​

CommonJS 的做法是,一旦出现某个模块被 " 循环加载 ",就只输出已经执行的部分,还未执行的部分不会输出

CommonJS 模块的重要特性是加载时执行,即脚本代码在 require 的时候,就会全部执行。一旦出现某个模块被 " 循环加载 ",就只输出已经执行的部分,还未执行的部分不会输出。

让我们来看,Node 官方文档 里面的例子。脚本文件 a.js 代码如下。

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

上面代码之中,a.js 脚本先输出一个 done 变量,然后加载另一个脚本文件 b.js。注意,此时 a.js 代码就停在这里,等待 b.js 执行完毕,再往下执行。

再看 b.js 的代码。

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

上面代码之中,b.js 执行到第二行,就会去加载 a.js,这时,就发生了 " 循环加载 "。系统会去 a.js 模块对应对象的 exports 属性取值,可是因为 a.js 还没有执行完,从 exports 属性只能取回已经执行的部分,而不是最后的值。

a.js 已经执行的部分,只有一行。

javascript
exports.done = false;

因此,对于 b.js 来说,它从 a.js 只输入一个变量 done,值为 false。

然后,b.js 接着往下执行,等到全部执行完毕,再把执行权交还给 a.js。于是,a.js 接着往下执行,直到执行完毕

ES6 模块的循环加载 ​

js
1.  /*a.js*/
2.  import { count } from './b.js'
3.  console.log(count);
4.  export let message = 'hello'
5.  /*b.js*/
6.  import { message } from './a.js'
7.  export let count = 5;
8.  setTimeout(() => {
9.      console.log(message);
10.  }, 0);

调用流程:
(1) 程序先进入 a.js,执行 import {count} from 'b.js',进入 b.js;

(2)b.js 中执行 import {message} from 'a.js',企图再次进入 a.js,但是 a.js 已经请求过,但没有解析完,被标记为 Fetching,

  • (内部有一个 Module Map,专门记录一个 Module 当前的状态,如果解析完成就获取它的 Module Record(类似 AST,会分析出该模块的 import,export,获得依赖关系);如果没有解析完成,则被标记为 Fetching,不做处理,继续执行。)
    此时从 a.js 中没有任何导出,无法获取 message(可以认为此时 message 为 undefined)。

(3)b.js 执行完毕,导出了 count,在 a.js(b.js 的上层)中找到 count,将它们链接起来 (指向同一个地址)

(4) 返回 a.js 中继续执行,导出了 message,在 b.js(a.js 的上层)中找到 message,将它们链接起来 (指向同一个地址)

(5)b.js 中的 setTimeout 执行,得到了 a.js 中导出的 message

区别 ​

ES module 趋向于构建依赖树,它会沿着一个入口,根据 import 关系 (利用 AST 分析) 去构建一棵依赖树,遍历到树的叶子模块后,然后根据依赖关系,反向(向上)找到父模块,将 export/import 指向同一地址。

而 commonJS 的导出则简单的多,它将每个模块的导出视为一个对象,在刚进入模块的时候,就为它准备好了一个空对象作为它的导出结果,如果有导出就在这个对象上增加 key,value。因此,别的模块得到的引用对象则仅仅只是这个导出对象的引用。

参考 ​

JavaScript 模块的循环加载 - 阮一峰的网络日志

CommonJS和ES6模块循环加载处理的区别 - 掘金

贡献者

The avatar of contributor named as jiechen jiechen

页面历史

用 ♥ 撰写