ECMAScript 模块

CommonJS 模块很好用,并且与 NPM 一起,使 JavaScript 社区开始大规模共享代码。

但他们仍然是个简单粗暴的黑魔法。 例如,表示法有点笨拙 - 添加到exports的内容在局部作用域中不可用。 而且因为require是一个正常的函数调用,接受任何类型的参数,而不仅仅是字符串字面值,所以在不运行代码就很难确定模块的依赖关系。

这就是 2015 年的 JavaScript 标准引入了自己的不同模块系统的原因。 它通常被称为 ES 模块,其中 ES 代表 ECMAScript。 依赖和接口的主要概念保持不变,但细节不同。 首先,表示法现在已整合到该语言中。 你不用调用函数来访问依赖关系,而是使用特殊的import关键字。

  1. import ordinal from "ordinal";
  2. import {days, months} from "date-names";
  3. export function formatDate(date, format) { /* ... */ }

同样,export关键字用于导出东西。 它可以出现在函数,类或绑定定义(letconstvar)的前面。

ES 模块的接口不是单个值,而是一组命名绑定。 前面的模块将formatDate绑定到一个函数。 从另一个模块导入时,导入绑定而不是值,这意味着导出模块可以随时更改绑定的值,导入它的模块将看到其新值。

当有一个名为default的绑定时,它将被视为模块的主要导出值。 如果你在示例中导入了一个类似于ordinal的模块,而没有绑定名称周围的大括号,则会获得其默认绑定。 除了默认绑定之外,这些模块仍然可以以不同名称导出其他绑定。

为了创建默认导出,可以在表达式,函数声明或类声明之前编写export default

  1. export default ["Winter", "Spring", "Summer", "Autumn"];

可以使用单词as重命名导入的绑定。

  1. import {days as dayNames} from "date-names";
  2. console.log(dayNames.length);
  3. // → 7

另一个重要的区别是,ES 模块的导入发生在模块的脚本开始运行之前。 这意味着import声明可能不会出现在函数或块中,并且依赖项的名称只能是带引号的字符串,而不是任意的表达式。

在撰写本文时,JavaScript 社区正在采用这种模块风格。 但这是一个缓慢的过程。 在规定格式之后,花了几年的时间,浏览器和 Node.js 才开始支持它。 虽然他们现在几乎都支持它,但这种支持仍然存在问题,这些模块如何通过 NPM 分发的讨论仍在进行中。

许多项目使用 ES 模块编写,然后在发布时自动转换为其他格式。 我们正处于并行使用两个不同模块系统的过渡时期,并且能够读写任何一种之中的代码都很有用。