Dynamic Module Reform ?
中文标题:动态模块改革
- 阶段: 未分阶段
- 状态: 已撤回
- ECMAScript 版本: —
- 同步时间: 2026年8月26日
- English original · 官方仓库
该提案旨在解决在 ES 模块链接算法下,动态模块(如 CommonJS 模块)难以保留执行顺序的问题。它在 ResolveExport() 中引入了 'pending' 解析值,并在源文本模块记录上新增 \[\[PendingImportEntries\]\] 内部槽,将对尚未求值的动态模块绑定的验证延迟到 ModuleEvaluation() 阶段。这还支持 ES 模块与动态模块之间的循环依赖,与 CommonJS 的行为保持一致。
以下 README 来自上游仓库,其中的阶段或状态标注可能滞后;当前信息以提案概览为准。
动态模块改革(已否决)
ECMAScript 提案规范,用于保留动态模块执行顺序的改革。
提案发起人
该提案已被否决,以保留现有语义。你可以在 tc39-notes 中阅读更多关于这一决定的信息。Update 2 - March 2017 中的最后一张幻灯片说明了该提案为何存在缺陷。
幻灯片
理由
ES 模块规范要求所有模块记录(Module Records)在 ModuleDeclarationInstantiation() 期间知道所有导出。这是为了保证链接过程(从一个模块环境记录到另一个模块记录的绑定)能够完成,并且这对源文本模块记录(Source Text Module Records)非常适用,但会强制其他动态模块(如 Node CJS 模块)在 ModuleDeclarationInstantiation() 阶段被求值。因此,无法保留求值顺序。
保留动态模块记录(Dynamic Module Records)的求值顺序
本提案在针对动态模块记录(Dynamic Module Record)调用 ResolveExport() 时引入了 "pending" 解析值。此外,本提案在源文本模块记录(Source Text Module Records)上引入了一个新的内部槽 [[PendingImportEntries]],用于跟踪来自尚未求值的动态模块记录的导入条目。因此,在 ModuleDeclarationInstantiation() 阶段对动态模块记录调用 ResolveExport() 可以解析为 "pending",以表示对绑定的验证或断言应延迟到从动态模块记录导入的源文本模块记录的 ModuleEvaluation() 阶段。
这些更改使我们能够在链接阶段识别出尚无法创建且在此阶段不需要显式断言的导入绑定。因此,我们可以延迟动态模块记录的求值以保留执行顺序,并且我们是在假设动态模块记录中的导入绑定在其被求值后会被填充的前提下这样做的。
支持动态模块记录(Dynamic Module Records)中的循环依赖
这一更改还使我们能够在循环依赖方面匹配 NCJS 的语义。以下示例说明了这一点:
这些 CJS 模块在 Node 中无论先导入哪一个都能正常工作;如果两者都写成 ESM,情况也是如此。但如果一个是 ESM,另一个是 CJS,根据当前规范,我们可能会因为先导入哪一个而得到静态错误。例如:
如果 even.js 中 odd 的绑定直到 odd.js 被求值之后才创建,那么 NCJS 语义得以保留,并且该示例无论先导入哪一个都能正常工作。
源文本模块记录中的 SyntaxError
- 如果间接导入条目解析为 null 或 ambiguous,则在
ModuleDeclarationInstantiation期间抛出。 - 如果在求值所有依赖之后、求值源文本之前,待处理导入条目解析为 null、ambiguous 或 pending,则在
ModuleEvaluation期间抛出。
折衷方案
有了这个提案,ESM 引入的静态可验证机制只能在从一个 ES 模块导入另一个 ES 模块时强制执行,而与动态模块记录的任何交互都将被延迟到 ModuleEvaluation() 阶段,针对那些先前未被求值的动态模块记录。
附加信息
围绕该主题的大部分讨论都集中在 TC39 2016 年 9 月会议的会议记录中:
规范
你可以查看渲染为 HTML 的规范。