ES Module 和 ES 2015 并不挂钩

ES Module 和 ES 2015 并不挂钩

很多人似乎往往误以为只要是 ES2015 的代码,就是能够使用 ES Module(import、export)的,这是一个彻头彻尾的误解。

ES2015 并不是在原有的 ECMAScript 基础上增加了 ES Module 的语言特性,而是重新引入了一种与原有 ECMAScript 文件类型不相容的文件类型;新的文件类型叫做 Module,原有的文件类型叫做 Script(不要和 HTML 的 <script> tag 混淆)。


ecma-international.org/

也就是说,只有 Module 的文件类型才包含 ES Module 的功能,而 Script 依然是没有 ES Module 的功能的。所以尽管都是 ES2015 的代码,只有以 Module 方式引入的 ES2015 代码才具备 import、export 的语法,而通过 Script 方式引入的 ES2015 代码永远也无法使用 import、export,之前与之后的版本也是一样。

那么有没有可能同一个 ECMAScript 文件既是合法的 Script 又是合法的 Module 呢?答案是可以的,Module 中虽然可以使用 import、export,但并没有要求必须使用 import、export,所以一个不使用 import 和 export 的 Module 类型往往也是合法的 Script 类型。也就是说,无法通过语法层面的分析来确定一个 ECMAScript 文件到底是 Module 还是 Script(另外即便用了 import、export 也可以是非法的 Script,为什么一定要合法呢?),现有的很多构建工具通过关键字检测的方式来区分 Module 和 Script 是非常不严谨的方式。

不过,虽然语法上存在同时可以作为合法的 Module 以及合法的 Script 的 ECMAScript 文件,但在这两种不同文件类型下的语义却是不同的,比如是否为严格模式以及外层变量的作用域。所以既不能根据语法来区分,也不能不区分,必须通过某种方式手动指定。

说了这么多,那么到底如何确定一个 ECMAScript 文件到底是作为 Script 引入的还是作为 Module 引入的呢?这是一个很好的问题,因为 ECMAScript 规范自己也没有说。不过,可以确定的一点,为了保持向前兼容性,所有 ECMAScript 出现前能够正确工作的引入 ECMAScript 文件的方式都是 Script 的方式。所以说,只要是通过:

<script type="text/javascript"><script>

引入的代码,那就是无法使用 ES Module 的,不论是语言是 ES2015、ES2016 还是 ES2017。

运行环境为了保持兼容性,都必须提供新的方式来引入 Module 的代码,例如 HTML 中就是使用:

<script type="module"><script>

不过要注意,这里的 module 并不是 MIME 类型,只是 HTML 规范中专门为 ES Module 指定的,仅可用于 script[type] 的一个特殊值。

而其它环境,比如 Node.js,似乎为如何引入 Module 文件类型进行了相当长时间的争论,目前似乎还没有最终定论。(更新,已经确定为使用 .mjs 文件后缀区分)

此外,目前通过 Script 方式引入的文件中也是无法与通过 Module 方式引入的文件直接交互的。有一个已经在 Stage 3 的 Dynamic import 提案能够解决这个问题,另外 Web 上也有 WHATWG 的 Loader 提案。

所以,“为什么明明支持 ES2015 但还是不能用 ES Module?” 这种问题是没有任何意义的,如果一个运行环境的引入方式是 Script,那它根本就不能支持 ES Module,支持了反而是错的。至于各个运行环境什么时候能支持 Module 的引入方式,那是另外一个问题了。

编辑于 2018-01-15

文章被以下专栏收录