Circular dependencies are no problem for declarative imports/exports. In your case, the circle is of minimal length though :-)
The solution is that an import
does not import a value into a variable, but that it makes a variable a reference to the exported variable. Have a look here for an example of a mutable variable, and at this question for exact terminology.
And it's the same for module namespace objects - their properties are just getters that resolve to the actual exported variable.
So when your module is loaded and evaluated, the following steps occur:
- The source is statically analysed for
export
and import
declarations to build a dependency graph
- The module scope is created
- Since the only dependency of your module is itself, and that already is getting initialised, it doesn't need to wait for it
- The
fooModule
variable is created and instantiated to an object with the exported names of the module, which are known to be ["logFoo"]
. The fooModule.logFoo
property becomes a getter that will evaluate to the logFoo
variable in the module scope (if you had used export {A as B}
, then fooModule.B
would resolve to A
, but in your case both names are the same).
- The variable declarations in the module scope create the variables, in your case
logFoo
, and function declarations are initialised (i.e. logFoo
gets assigned the function)
- The module code is run (in your case, nothing happens)
Now when you call logFoo
in a module that imports it, fooModule
will refer to the namespace that contains logFoo
. No magic :-)