For AI agents: the complete documentation index is available at /tc39-atlas/llms.txt, the full documentation bundle is available at /tc39-atlas/llms-full.txt, and this page is available as Markdown at /tc39-atlas/proposals/stage/unstaged/proposal-UnambiguousJavaScriptGrammar.md.
  • 简体中文
  • Proposed Grammar change to ES Modules ?

    中文标题:ES 模块的拟议语法变更

    提案概览
    提案速览

    该提案解决了 ECMAScript 中 Script 和 Module 解析目标之间的歧义,其中代码可以在任一日标下运行但产生不同的结果。它要求模块至少有一个 import 或 export 声明来消除歧义,并建议对 .js 文件进行回退解析。该提案于 2016 年 7 月被 Node 接受,并使用 package.json 配置和多次解析尝试来确定目标,从而改善工具和性能。

    Note

    以下 README 来自上游仓库,其中的阶段或状态标注可能滞后;当前信息以提案概览为准。

    无歧义 JavaScript 语法

    状态草案
    作者@jdalton @bmeck
    日期2016 年 6 月 14 日

    提案已接受!

    2016 年 7 月 6 日,此提案被接受为 Node ES6 模块互操作性 草案提案的 第 5.1 节 :bangbang:

    概要

    • CJS 和 ES 模块直接可用,无需新的扩展、额外仪式或过度脚手架
    • 性能通常与现有 CJS 模块加载相当
    • 与转译工作流相比,ES 模块的性能显著提高
    • 将 Script 和 Module 的 JavaScript 语法改为无歧义
    • 通过先按一种语法解析,再回退到其他语法来确定 .js 文件的语法

    问题

    ECMA262 的 Script 和 Module 目标存在语法歧义,某些代码可以在两种目标下运行,具有完全相同的源文本,但产生不同的结果。与 "use strict" 不同,指示特定行为的信号不在代码中,因此代码具有多种可能的效果,而这些效果不受程序员控制。

    注意:以下示例突出显示了歧义语法的一些影响,但绝非详尽无遗。

    示例

    function foo(value) {
      value = value || '';
      var args = [].slice.call(arguments);
      args.unshift(this);
      return args;
    }
    foo(null);

    差异:

    scriptmodule
    foo 的变量作用域全局局部
    arguments 对象已修改未修改
    foothis 绑定全局undefined

    由于在当前语法下,无法在源文本中强制指定目标,这导致某些构造的行为由程序员未定义,而由宿主环境定义。反过来,现有代码可能在错误的目标下运行,部分功能正常,或者运行无错误但产生不正确的结果。

    ECMA262 解决方案

    要求 Module 源文本至少包含一个 importexport 声明。仅包含 import 声明而没有 export 声明的模块是有效的。不导出任何内容的模块应指定 export {} 以明确意图,并避免在移除 import 声明时出现意外解析错误。export {} 不是新语法,也导出空对象。它只是指定不导出任何内容的标准方式。

    注意:虽然 ES2015 规范 不禁止 此扩展,但 Node 希望避免充当流氓代理。Node 在 TC39 有代表 @bmeck 来推动此提案。欢迎进行规范更改或至少正式认可此 Node 提案。如果无法达成解决方案,此提案将回退到之前的 .mjs 文件扩展名提案

    Script 示例

    function foo(value) {
      value = value || '';
      var args = [].slice.call(arguments);
      args.unshift(this);
      return args;
    }
    foo(null);
    scriptmodule(无法解析)
    foo 的变量作用域全局不适用
    arguments 对象已修改不适用
    foothis 绑定全局不适用

    Module 示例

    function foo(value) {
      value = value || '';
      var args = [].slice.call(arguments);
      args.unshift(this);
      return args;
    }
    foo(null);
    export {};
    script(无法解析)module
    foo 的变量作用域不适用局部
    arguments 对象不适用未修改
    foothis 绑定不适用undefined

    问题

    Node 目前需要一种方法让程序员指示其代码运行的目标。

    主要解决方案要么有沉重的生态系统代价、仪式或脚手架。它们缺乏从 ECMA262 标准定义源文本意图的方法。

    解决方案

    包通过在其 package.json 中指定 "module" 作为解析目标字段 (名称未最终确定) 来选择 Module 目标。包依赖不受此选择影响,可以是 CJS 和 ES 模块包的混合。如果未指定解析目标,则尝试将源文本解析为优先目标 (目前为 Script,因为大多数模块是 CJS)。如果出现可能允许其他目标解析的解析错误,则按其他目标解析,依此类推。此后,目标被无歧义地确定,环境可以安全地执行初始化,而不会使源文本在错误的目标下运行。

    算法

    Parse (source, goal, throws)

    将源文本解析为给定目标的抽象操作。

    1. goal 引导 source
    2. source 解析为 goal
    3. 如果成功,返回 true
    4. 如果 throws,抛出异常。
    5. 返回 false
    操作
    1. 如果指定了包解析目标,则

    2. goal 为解析后的解析目标。

    3. 调用 Parse(source, goal, true) 并返回。

    4. 否则回退到多次解析。

    5. 如果 Parse(Source, Script, false)true,则 1. 返回。

    6. 否则 1. 调用 Parse(Source, Module, true)

    注意:主机可以选择任一目标先解析,并可能随时间或引入新解析目标时更改其顺序。可以随意交换 Script 和 Module 的顺序。

    实现

    为了提高性能,宿主环境可能希望指定首先解析的目标。这可以通过多种方式完成:
    磁盘缓存、命令行标志、清单文件、HTTP 头、文件扩展名等。

    工具关注点

    Node 之外的一些工具可能无法访问 JS 解析器 (Bash 程序、某些资源管道等)。这些工具通常将文件视为不透明 blob / 纯文本文件,可以使用 实现 下列出的技术来获取解析目标信息。

    外部示例和影响

    • Esprima 依赖 --module 标志来表示 Module 目标。然而,这对许多用户来说已被证明是不直观的。无歧义的 Script 和 Module 目标将使事情“直接可用”,无需标志。

    • Facebook Flow 执行一系列推断来检测 CJS 和 ES 模块。无歧义的 Script 和 Module 目标将提高其确定模块类型的能力。

    • JSCS 可以通过 stdin 接受输入,因此通过源文本识别解析目标是理想的。

    • 像 xo 这样的 linter 可以使用无歧义的 Script 和 Module 目标来启用特定于模块的 lint 规则,而无需额外配置。

    • 微软打包的 Web 应用程序可以受益于无歧义的 Script 和 Module 目标。打包的 Web 应用程序的字节码缓存在安装时生成。当应用程序运行时,文件通过脚本标签加载,因此其预期的解析目标是已知的。然而,字节码缓存的生成是在 运行应用程序的情况下完成的,因此预期的解析目标是未知的。因此,字节码缓存为 Script 目标生成,而 ES 模块则被忽略。

    • TypeScript 从早期就采取了这样的立场:当脚本至少有一个 importexport 声明时,它就成为一个模块。多年来,这种方法几乎没有遇到用户问题。

    特别感谢

    没有 @bmeck@dherman@wycats 的不懈努力、坚定信念和协作,本提案不可能实现。

    在迭代此提案时,我们联系了受其影响的多个领域的数人。
    尽管对此提案的意见各不相同,但我感谢以下人士的反馈:

    谢谢! :heart:
    @jdalton