ES module循环

    ES module是ES6官方发布的Module特性,利用export/import实现导出/导入。我们看看下面的结果(在script上加上type='module',即可实现ES module):

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

    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环境中执行):

    1. /*c.js*/
    2. var count=require('./d.js').count;
    3. console.log(count);
    4. exports.message='hello';
    5. /*d.js*/
    6. var message=require('./c.js').message;
    7. exports.count=5;
    8. setTimeout(function(){
    9.     console.log(message);
    10. },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为啥得不到正确的结果?我们看看下面的演示:

    1. var o={};//进入c.js,初始化导出对象为空对象
    2. var m=o.message;//进入d.js,先获取message
    3. setTimeout(function(){//d.js中,定时获取message内容
    4.     console.log(m);
    5. },0);
    6. o.message='hello';//回到c.js中,重新给message赋值

    上面的演示就是按照commonJS导入/导出逻辑来的,我们很容易发现结果是不对的,因为第一个获取的message和第二次赋值的message没有任何关系,自然也就得不到正确结果了。

    如果想让上面的commonJS循环得到正确的结果,可以改写如下:

    1. /*c.js*/
    2. var count=require('./d.js').count;
    3. console.log(count);
    4. exports.message='hello';
    5. /*d.js*/
    6. var obj=require('./c.js');
    7. exports.count=5;
    8. setTimeout(function(){
    9.     console.log(obj.message);
    10. },0)

    commonJS中直接获取导出对象,然后在访问导出内容,可以得到正确结果。

    回到顶部
    我要评论

    所有评论

      相关文章