mqklin
mqklin

Reputation: 2048

Export objects and classes before their declaration makes them undefined

I try to repeat example from Mozilla Hacks (Export lists subtitle):

//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

Oops: Kittydar is undefined (btw., the problem is the same with simple Object).

But if I put export after Kittydar declaration it's ok:

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

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

Is this a typo in the article?

I transpile this with babel and bundle with browserify. Then I include output bundle in a usual .html file (with <script> tag).

Upvotes: 5

Views: 746

Answers (1)

Jason Orendorff
Jason Orendorff

Reputation: 45116

The standard is hard to follow on this, but the article is correct. This code works in es6draft and in the SpiderMonkey shell: both the function and the class are initialized by the time those console.log calls run.

Here's how it's supposed to work, in minute detail:

  • The JS engine parses import.js. It sees the import declaration, so then it loads export.js and parses it as well.

  • Before actually running any of the code, the system creates both module scopes and populates them with all the top-level bindings that are declared in each module. (The spec calls this ModuleDeclarationInstantiation.) A Kittydar binding is created in export.js, but it's uninitialized for now.

    In import.js, a Kittydar import binding is created. It's an alias for the Kittydar binding in export.js.

  • export.js runs. The class is created. Kittydar is initialized.

  • import.js runs. Both console.log() calls work fine.


Babel's implementation of ES6 modules is nonstandard.

I think it's deliberate. Babel aims to compile ES6 modules into ES5 code that works with an existing module system of your choice: you can have it spit out AMD modules, UMD, CommonJS, etc. So if you ask for AMD output, your code might be ES6 modules, but the ES5 output is an AMD module and it's going to behave like an AMD module.

Babel could probably be more standard-compliant while still integrating nicely with the various module systems, but there are tradeoffs.

Upvotes: 6

Related Questions