ES6 的 import 语法中,一个模块是如何被准确求值的?

In the `import` syntax of ES6, how is a module evaluated exactly?

假设我们有四个模块,ABCD

在模块 A 中:

console.log("A evaluated")
function AClass {
  console.log("A constructor")
}
var aObj = new AClass()
export default aObj;

在模块 B 中:

import aObj from A
export default "B"

在模块 C 中:

import aObj from A
export default "C"

在模块 D 中:

import b from B
import c from C
import aObj from A

那么当模块 D 被评估时, A evaluatedA constructor 将在控制台中打印多少次?

ES6 标准 中是否描述了这种行为?如果我想让一个模块无论直接或间接导入多少次都只被评估一次,该怎么办?有人对此有任何想法吗?

执行D模块时,控制台会打印出这样的信息:

A evaluated
A constructor

这意味着 A 模块仅被评估一次,即使它被其他模块多次导入也是如此。

ES6 modules的评估规则与commonjs相同格式:

  • 模块是一段代码,一旦加载就会执行。这意味着如果一个模块不包含在主包中,它将不会被评估
  • 模块是单例的。如果多次导入一个模块,则只有一个 instance 存在,并且仅在加载时评估一次

ECMAScript 6 规范的 HostResolveImportedModule 部分描述了 导入模块的相同实例 的行为。
它提到:

This operation (import operation) must be idempotent if it completes normally. Each time it is called with a specific referencingModule, specifier pair (import <a> from <source>) as arguments it must return the same Module Record instance.

模块的单次评估 的行为在ModuleEvaluation、第 4 点和第 5 点使用 Evaluated 布尔标志进行了描述。
每个模块都有 Evaluated 标志,确保只评估模块代码一次。