43

I know that in the new ES6 module syntax, the JavaScript engine will not have to evaluate the code to know about all the imports/exports, it will only parse it and “know” what to load.

This sounds like hoisting. Are the ES6 modules hoisted? And if so, will they all be loaded before running the code?

Is this code possible?

import myFunc1 from 'externalModule1';

myFunc2();

if (Math.random()>0.5) {
    import myFunc2 from 'externalModule2';
}
Michał Perłakowski
  • 88,409
  • 26
  • 156
  • 177
gilamran
  • 7,090
  • 4
  • 31
  • 50

3 Answers3

42

After doing some more research, I've found:

  • Imports ARE hoisted! according to the spec of ModuleDeclarationInstantiation
  • ALL the dependent Modules will be loaded before running any code.

This code will have no errors, and will work:

localFunc();

import {myFunc1} from 'mymodule';

function localFunc() { // localFunc is hoisted
    myFunc1();
}
gilamran
  • 7,090
  • 4
  • 31
  • 50
  • 8
    The given example does not demonstrate hoisting by the import. myFunc1 is only used in the scope below the import. The localFunc itself is hoisted, and that is all this example demonstrates. – Constablebrew Nov 10 '16 at 17:46
  • 10
    if calling localFunc doesn't throw an error when it calls myFunc1 then it demonstrates that imports are hoisted. – Shanimal Dec 31 '16 at 16:09
  • 1
    It seems Typescript does not hoist imports though – Tamas Hegedus Jun 17 '22 at 09:25
29

It will be a SyntaxError. According to this part of specification:

Module :
   ModuleBody

ModuleBody :
    ModuleItemList

ModuleItemList :
    ModuleItem
    ModuleItemList ModuleItem

ModuleItem :
    ImportDeclaration
    ExportDeclaration
    StatementListItem

It means that module can contain only ImportDeclaration's, ExportDeclaration's or StatementListItem's. According to this StatementListItem could not contain ImportDeclaration nor ExportDeclaration.

import myFunc1 from 'externalModule1'; 

is an import declaration, while:

if (Math.random()>0.5) {
    import myFunc2 from 'externalModule2';
}

is a statement. So your code will result to a syntax error.

What about "will they all be loaded before running the code?". This part of specification contain next sentence:

NOTE: Before instantiating a module, all of the modules it requested must be available.

So, yeah. They will all be loaded before running the code.

Shanimal
  • 11,517
  • 7
  • 63
  • 76
alexpods
  • 47,475
  • 10
  • 100
  • 94
  • alexpods, thanks for clearing it up, but I've managed to hoist an import without putting it inside "if" statement. see my answer. – gilamran Mar 29 '15 at 21:00
3

ES6 specification is a subject to change but this draft is explicit:

The static variable resolution and linking pass checks for conflicts in imported variable names. If there is a conflict between two imported names, or an imported name and another local binding, then it is a compile-time error.

And trying to import at runtime is doubtful idea, not only in ES6. Also from the draft:

Compilation resolves and validates all variable definitions and references. Linking also happens at compile-time; linking resolves and validates all module imports and exports.

You can see that Babel's ES6 implementation isn't too happy with it.

Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • 1
    It is proposed though that `System.require("modulename")` or so will allow dynamic module loading, returning a promise for the module. It won't be linked into the module scope, of course. – Bergi Mar 29 '15 at 16:12