ES6的模块循环加载

我在看阮一峰的ES6入门,https://es6.ruanyifeng.com/#d...

底下这个例子没有明白。 按照他的说法:我在执行a时发现从b中import bar然后去执行b,那么为什么b中第一句话不去执行a而是默认这个接口已经存在?感觉这好矛盾啊。

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

图片描述

阅读 2.6k
1 个回答
首先,执行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

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

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题