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中直接获取导出对象,然后在访问导出内容,可以得到正确结果。

回到顶部
我要评论

所有评论

返回
邮箱:
绑定
取消
×

我要评论

回复:

昵称:(昵称不超过20个字)

图片:

邮箱:
绑定邮箱后,若有回复,会邮件通知。
提交
还可以输入500个字