说明
- commonjs是加载时运行、esmodule是异步运行(本身并不是真正设计成无条件异步的。而是设计成只在条件下异步 - 只有当代码中包含顶级
await时才会异步) - commonjs输出的是值的浅拷贝、esmodule是输出值的引用
互转
https://juejin.cn/post/7205897684624474168
其他
commonjs和ES module循环引用链接
ES module是ES6官方发布的Module特性,利用export/import实现导出/导入。我们看看下面的结果(在script上加上type=‘module’,即可实现ES module):
- /a.js/
- import { count } from ‘./b.js’
- console.log(count);
- export let message = ‘hello’
- /b.js/
- import { message } from ‘./a.js’
- export let count = 5;
- setTimeout(() ⇒ {
- console.log(message);
- }, 0);
ES module循环(二维码)
调用流程:
(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。
commonJS循环
commonJS是nodeJS中的模块引用,利用require/exports实现导出/导入,看看下面的结果(在nodeJS环境中执行):
- /c.js/
- var count=require(‘./d.js’).count;
- console.log(count);
- exports.message=‘hello’;
- /d.js/
- var message=require(‘./c.js’).message;
- exports.count=5;
- setTimeout(function(){
- console.log(message);
- },0)
执行结果如下图:

调用流程:
(1)c.js执行require(‘./d.js’),进入d.js。
(2)d.js中执行require(‘./c.js’),企图再次进入c.js,但是c.js已经被加载过,因此require(‘./c.js’)会得到一个空对象。(内部给每个模块的导出都定义了一个对象,如果一个模块有导出,那么相当于这个导出对象上多了一组key,value)。此时的require(‘./c.js’).message为undefined。
(3)d.js执行完,导出了count;c.js执行完,导出message。
(4)d.js中的setTimeout执行,但是message仍然为undefind。
区别
ES module趋向于构建依赖树,它会沿着一个入口,根据import关系(利用AST分析)去构建一棵依赖树,遍历到树的叶子模块后,然后根据依赖关系,反向(向上)找到父模块,将export/import指向同一地址。
而commonJS的导出则简单的多,它将每个模块的导出视为一个对象,在刚进入模块的时候,就为它准备好了一个空对象作为它的导出结果,如果有导出就在这个对象上增加key,value。因此,别的模块得到的引用对象则仅仅只是这个导出对象的引用。
commonJS获取正确结果
上面的commonJS为啥得不到正确的结果?我们看看下面的演示:
- var o={};//进入c.js,初始化导出对象为空对象
- var m=o.message;//进入d.js,先获取message
- setTimeout(function(){//d.js中,定时获取message内容
- console.log(m);
- },0);
- o.message=‘hello’;//回到c.js中,重新给message赋值
上面的演示就是按照commonJS导入/导出逻辑来的,我们很容易发现结果是不对的,因为第一个获取的message和第二次赋值的message没有任何关系,自然也就得不到正确结果了。
如果想让上面的commonJS循环得到正确的结果,可以改写如下:
- /c.js/
- var count=require(‘./d.js’).count;
- console.log(count);
- exports.message=‘hello’;
- /d.js/
- var obj=require(‘./c.js’);
- exports.count=5;
- setTimeout(function(){
- console.log(obj.message);
- },0)
commonJS中直接获取导出对象,然后在访问导出内容,可以得到正确结果。