在声明未定义之前导出 objects 和 类

Export objects and classes before their declaration makes them undefined

我尝试重复 Mozilla Hacks 中的示例(导出列表 副标题):

//export.js
export {detectCats, Kittydar};
function detectCats() {}
class Kittydar {}

//import.js
import {detectCats, Kittydar} from "./export.js";
console.log(detectCats); // function detectCats() {}
console.log(Kittydar); // undefined

糟糕:Kittydar 未定义(顺便说一句,问题与简单 Object 相同)。

但是如果我在 Kittydar 声明之后放置 export 就可以了:

//export.js
class Kittydar {}
export {Kittydar};

//import.js
import {Kittydar} from "./export.js";
console.log(Kittydar); // function Kittydar() {_classCallCheck(this, Kittydar);}

这是文章中的错字吗?

我用 babel 编译它并用 browserify 捆绑。然后我将输出包包含在一个通常的 .html 文件中(带有 <script> 标签)。

这个标准很难遵循,但是这篇文章是正确的。此代码在 es6draft 和 SpiderMonkey shell 中有效:函数和 class 都在 console.log 调用 运行.[=19 时初始化=]

这是它应该如何工作的详细信息:

  • JS引擎解析import.js。它看到 import 声明,因此它加载 export.js 并解析它。

  • 在实际 运行 任何代码之前,系统会创建两个模块范围并使用每个模块中声明的所有 top-level 绑定填充它们。 (规范称之为 ModuleDeclarationInstantiation。)在 export.js 中创建了 Kittydar 绑定,但目前尚未初始化。

    在 import.js 中,创建了一个 Kittydar 导入绑定 。它是 export.js 中 Kittydar 绑定的别名。

  • export.js运行秒。 class 已创建。 Kittydar 已初始化。

  • import.js运行秒。 console.log() 两个调用都工作正常。


Babel 对 ES6 模块的实现是非标准的。

我认为这是故意的。 Babel 旨在将 ES6 模块编译成 ES5 代码,该代码可与您选择的现有模块系统一起使用:您可以让它吐出 AMD 模块、UMD、CommonJS 等。因此,如果您要求 AMD 输出,您的代码可能是 ES6 模块,但是 ES5 输出是一个 AMD 模块,它的行为将像一个 AMD 模块。

Babel 可能会更好 standard-compliant,同时仍然与各种模块系统很好地集成,但需要权衡取舍。