所以最近我想到了ES模块,这就是我认为它们起作用的方式:
#moduleMap
)。它将模块的绝对URL映射到其导出:#moduleMap = {
"https://something.com/module.mjs": { exportName: "value" }
}
import { x } from "./module1.mjs"
=>所有x
引用均替换为
#moduleMap["abs path module1.mjs"].x
(正在提取导入的模块)
export const y = "some value"
=> #moduleMap["abs path to this module"].y = "some value"
(正如@Bergi指出的那样,出口并不是那么简单,因为没有悬挂出口,因此const的死区和函数的悬挂不仅仅反映在属性分配中)
(上面是产生“实时绑定”的所谓绑定)
正如@Bergi指出的那样,在执行模块代码本身之前(从循环依赖中除外),从入口模块开始急切地评估模块,并评估模块的导入(实际上,循环依赖除外),这实际上意味着最先执行的导入将首先执行。
#moduleMap["some module"]
的任何代码时,浏览器将检查模块是否已评估
#moduleMap
来访问导入基本上,这一切都是在发生AFAIK。我说得对吗?
答案 0 :(得分:2)
您了解得很好,但是有一些异常应该纠正。
在ECMA-262中,所有模块都具有以下一般形状:
Abstract Module {
Environment // Holds lexical environment for variables declared in module
Namespace // Exotic object that reaches into Environment to access exported values
Instantiate()
Evaluate()
}
模块可以来自很多不同的地方,因此此Abstract Module有“子类”。我们在这里所说的称为Source Text Module。
Source Text Module : Abstract Module {
ECMAScriptCode // Concrete syntax tree of actual source text
RequestedModules // List of imports parsed from source text
LocalExportEntries // List of exports parsed from source text
Evaluate() // interprets ECMAScriptCode
}
在模块(const a = 5
中声明变量时,该变量将存储在模块的Environment
中。如果在其中添加export
声明,它也会显示在LocalExportEntries中。
当您import
模块时,实际上是在抓取Namespace
对象,该对象具有 exotic 行为,这意味着尽管它看起来像是普通对象,但类似获取和设置属性可能会执行与您预期不同的事情。
在Module Namespace Objects的情况下,获取属性namespace.a
,实际上是在关联的Environment
中将该属性作为名称查找。
所以如果我有两个模块A和B:
// A
export const a = 5;
// B
import { a } from 'A';
console.log(a);
模块B导入A,然后在模块B中将a
绑定到A.Namespace.a
。因此,每当在模块{{1}中访问a
时,它实际上就会在b
上进行查找,而后者会在A.Namespace
中进行查找。 (这是实时绑定的实际工作方式。)
最后进入模块图的主题。所有模块都将被实例化,然后才能进行评估。实例化是解析模块图并准备要评估的模块的过程。
“模块映射”的概念是特定于实现的,但是出于浏览器和节点的目的,它看起来像这样:A.Environment
。
动态Module Map <URL, Abstract Module>
是显示浏览器/节点如何使用此模块映射的好方法:
import()
您实际上可以在Node.js中看到此确切行为:https://github.com/nodejs/node/blob/e24fc95398e89759b132d07352b55941e7fb8474/lib/internal/modules/esm/loader.js#L98
答案 1 :(得分:1)
export const y = "some value"
=>#moduleMap["abs path to this module"].y = "some value"
(上面是产生“实时绑定”的所谓绑定)
是的,基本上-您正确地理解它们都引用相同的内容,并且当模块重新分配时,进口商将注意到这一点。
但是,由于const y
保留了const
变量声明,所以它有点复杂,因此它仍然受时间死区的约束,function
声明仍受悬挂。当您将导出视为对象的属性时,这不能很好地反映出来。
- 当评估达到访问
#moduleMap["some module"]
的任何代码时,浏览器将检查模块是否已评估
- 如果未对它进行评估,则会在此时评估该评估,然后评估返回到该位置(现在模块(或确切地说是其导出)已“缓存”在
#moduleMap
中)- 如果已对其进行评估,则可以从
#moduleMap["some module"].someImport
来访问导入
不。当解释器遇到导入值的第一个引用时,模块评估就不会延迟进行。而是严格按照import
语句的顺序评估模块(从输入模块开始)。在对模块的所有依赖关系进行评估之前(除了对其自身具有循环依赖关系之外),都不会开始对模块进行评估。