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 对象已修改未修改
    foo 的 this 绑定全局undefined

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

    ECMA262 解决方案

    要求 Module 源文本至少包含一个 import 或 export 声明。仅包含 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 对象已修改不适用
    foo 的 this 绑定全局不适用

    Module 示例

    function foo(value) {
      value = value || '';
      var args = [].slice.call(arguments);
      args.unshift(this);
      return args;
    }
    foo(null);
    export {};
    script(无法解析)module
    foo 的变量作用域不适用局部
    arguments 对象不适用未修改
    foo 的 this 绑定不适用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 从早期就采取了这样的立场:当脚本至少有一个 import 或 export 声明时,它就成为一个模块。多年来,这种方法几乎没有遇到用户问题。

    特别感谢

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

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

    谢谢! :heart:
    — @jdalton