ES6的模块循环加载_经验分享

 

首先,执行a.mjs以后,引擎发现它加载了b.mjs,因此会优先执行b.mjs,然后再执行a.mjs。接着,执行b.mjs的时候,已知它从a.mjs输入了foo接口,这时不会去执行a.mjs,而是认为这个接口已经存在了,继续往下执行。

总是先执行依赖是正确的,但是发现循环的时候,并不会继续执行下去。

而是认为这个接口以存在

这个接口确实存在了。Module 执行分 Parse, Instantiate, Evaluate 几步。

Parse 解析整个源代码,并收集依赖,以及所有被导入、导出的名字。

Instantiate 递归地加载依赖(Parse + Instatntiate),并创建所有全局变量、导入符号地绑定。(至此,foo 的绑定已经存在了)注意此步并不会执行代码。
模块间导入的绑定只知道模块间名字的对应就可以了,循环引用并不是问题,因为每个模块都有自己的导入导出的名字列表。
全局变量绑定的时候,初始化规则与其它地方是一样的:var 初始化为 undefined ,let 不初始化,函数直接初始化为函数本身。所以,foo 的绑定是存在的,并且可以被其它导入了此符号的模块找到,但是,并没有初始化,所以不能读写。

Evaluate 开始递归的执行代码(被导入的先执行),递归在没有依赖或遇到循环的时候停止。所以 b.mjs 先执行。a.mjs 由于还没有执行,foo 还处在没有初始化的状态(let 变量要执行到变量声明处才初始化),所有出错。

====================

用现在的 node 的话,import 要写 from "./a.mjs" ,扩展名不能省略。

然后,错误信息是:

ReferenceError: Cannot access 'foo' before initialization

不能访问未初始化的变量。