Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a difference between `export default x` and `export {x as default}`?

I understand that with ES6 module exports, a binding takes place between what is exported and what is imported, so that when the exported variable changes, the imported variable will demonstrate that change.

However, I've also read that the imported variable only carries the binding to the exported variable in certain circumstances.

My specific question is whether there is a difference in how the exported variables are bound in the following two scenarios...

// Scenario #1
let a = 5;
export default a;

// Scenario #2
let a = 5;
export { a as default };
like image 536
sfletche Avatar asked Sep 01 '16 16:09

sfletche


1 Answers

They are not the same in the general case, though they can behave the same in the case of functions and classes.

let a = 4;
export default a;

is equivalent to

let a = 4;
let *default* = a;
export {*default* as default};

meaning that

let a = 4;
export default a;

a = 5;

will leave 4 as the exported value, even though a inside the module has changed, whereas export {a as default}; would make the exported value 5.

The ECMAScript spec defines three different forms of export default, with some examples in this table http://www.ecma-international.org/ecma-262/7.0/#table-42 and in the main syntax declaration for the exports: http://www.ecma-international.org/ecma-262/7.0/#sec-exports

export default HoistableDeclaration
export default ClassDeclaration
export default [lookahead ∉ { function, class }] AssignmentExpression;

with HoistableDeclaration in this case mapping to function declarations and generator declarations.

If we look at the spec where it defines the mapping of in-file variable names, to exported names, http://www.ecma-international.org/ecma-262/7.0/#sec-exports-static-semantics-exportentries

ExportDeclaration: export default HoistableDeclaration
  Let names be BoundNames of HoistableDeclaration.
  Let localName be the sole element of names.
  Return a new List containing the Record {[[ModuleRequest]]: null,
    [[ImportName]]: null, [[LocalName]]: localName, [[ExportName]]: "default"}.

ExportDeclaration: export default ClassDeclaration
  Let names be BoundNames of ClassDeclaration.
  Let localName be the sole element of names.
  Return a new List containing the Record {[[ModuleRequest]]: null,
    [[ImportName]]: null, [[LocalName]]: localName, [[ExportName]]: "default"}.

ExportDeclaration: export default AssignmentExpression;
  Let entry be the Record {[[ModuleRequest]]: null, [[ImportName]]: null,
    [[LocalName]]: "*default*", [[ExportName]]: "default"}.
  Return a new List containing entry.

  NOTE
  "*default*" is used within this specification as a synthetic name for anonymous default export values.

BoundNames here returns the name of the function or class passed as the value, so in the first two cases

export default function fn(){}
// or 
export default function* fn(){}
// or
export default class cls {}

will export live bindings for the variables fn or cls.

You could also do

export default function(){}
// or 
export default function*(){}
// or
export default class {}

in which case, these will export the values without live bindings since they have no name.

In the last case of export default AssignmentExpression ;, this is what your example of export default a; satisfies. You can note that it has [[LocalName]]: *default* rather than [[LocalName]]: localName like the others. That is because export default a; does not recognize a as the name being exported, it processes it as the current value of a being the exported value. This is no different than export default 4;, it has no name from the point of view of the spec.

Essentially

export default function fn(){}

is equivalent to

function fn(){}
export {fn as default};

but

let a = 4;
export default a;

is not equivalent to:

let a = 4;
export {a as default};
like image 70
loganfsmyth Avatar answered Oct 18 '22 21:10

loganfsmyth