As I understand it (see section 16.3.2.1), ES6 allows different syntaxes for function / class export operands. The difference refers to whether the exported function needs to be interpreted at import as a function declaration, in which case you write: export default function () {} // (a)
or as a function expression: export default (function () {}); // (b)
.
As a possible related sidenote: I read that imports are hoisted, but I'm not really sure what that means in this context.
Taking the case of this example:
import foo from 'my_module'; // (c)
As I understand it, the above statement will save my exported function in a foo
variable. Is that variable hoisted, or what is, and when?
Most importantly, what is the difference (in terms of setting foo
) when my_module
exports the function using (a)
and when it exports it using (b)
?
Your question is a bit convoluted but I'll try my best to explain everything.
Let's first establish how modules work in general. A module has a set of exported names, each of which refer to a local variable in that module. The name of the export does not need to be the same as that of the local binding. One of the exported names can be
default
, for which there is special syntax (both in exporting and importing) dedicated for the case that a module only exports a single thing.Yes, import declarations are hoisted. Similarly to a
var
orfunction
(and actually like every other declaration) the identifierfoo
is available right from the beginning, before any statements in the module are executed. In fact the binding is even created before those of declaredvar
iables.The difference is how they are initialised:
var
s are initialised withundefined
function
s andfunction*
s are initialised with the function objectlet
,const
andclass
es are left uninitialisedimport * as …
) are initialised with a module object (whose properties are such pointers as well)The short answer: before everything else.
The long answer: it's not really set. It's a reference to the local variable in the imported module that you expect to hold the function. The local variable might change when it's not
const
- but we usually don't expect that of course. And normally it does contain that function already, because the imported module is completely evaluated before the module(s) that import it are. So if you fear there's a problem with var functionName = function() {} vs function functionName() {} you may be relieved - there is not.Now back to your title question:
Nothing special, the two aspects actually don't have much to do with each other:
export
declarations link an export name to a local variable in the module scopeOf course, there still are no good reasons not to use the more declarative function declarations everywhere; this is not different in ES6 modules than before. If at all, there might even be less reasons to use function expressions, as everything is covered by declarations:
Ok, the last two default export declarations are actually a bit different than the first two. The local identifier that is linked to the exported name
default
is notfoo
, but*default*
- it cannot be reassigned. This makes sense in the last case (where there is no namefoo
), but in the second-to-last case you should notice thatfoo
is really just a local alias, not the exported variable itself. I would recommend against using this pattern.Oh, and before you ask: Yes, that last default export really is a function declaration as well, not an expression. An anonymous function declaration. That's new with ES6 :-)
They are pretty much the same for every purpose. They're anonymous functions, with a
.name
property"default"
, that are held by that special*default*
binding to to which the exported namedefault
points to for anonymous export values.Their only difference is hoisting - the declaration will get its function instantiated at the top of the module, the expression will only be evaluated once the execution of module code reaches the statement. However, given that there is no variable with an accessible name for them, this behavior is not observable except for one very odd special case: a module that imports itself. Um, yeah.
You really shouldn't do either of these things anyway. If you want to use an exported function in your module, give it a name in its declaration.
Happens. It's allowed by the spec because it doesn't make extra exceptions to prohibit certain nonsensical things. It is not intended to be used. In this case the spec even explicitly disallows
function
andclass
expressions inexport default
statements and treats them as declarations instead. By using the grouping operator, you found a loophole. Well done. Don't abuse it.