Does importing a module mean embedding the code of the module at the line of the import statement?

58 views Asked by At

From ReferenceError: can't access lexical declaration 'X' before initialization - JavaScript | MDN, there is an example of invalid import:

a.js (entry module):

import { b } from "./b.js";

export const a = 2;

b.js:

import { a } from "./a.js";

console.log(a); // ReferenceError: Cannot access 'a' before initialization
export const b = 1;

MDN explains:

In this example, the imported variable a is accessed but is uninitialized, because the evaluation of a.js is blocked by the evaluation of the current module b.js.

I understand this as importing a module means embedding the code of the module at the line of the import statement. That is, the a.js becomes this at compile time:

console.log(a); // ReferenceError: Cannot access 'a' before initialization
const b = 1;

const a = 2;

Is this understanding correct? I don't see it's explained in import - JavaScript | MDN. I don't know how to test this because reordering the lines in a.js doesn't change the result due to hoisting.

3

There are 3 answers

3
Heiko Theißen On BEST ANSWER

The code from an imported module is not simply embedded ("pasted"), but lives in a separate closure. Although this is surely an oversimplification, I compare the module with a function and the export statement with its return statement:

function a_js() {
  var b = b_js(); // unnecessary line
  return 2;
}

function b_js() {
  var a = a_js();
  console.log(a);
  return 1;
}
<button onclick="a_js()">import a.js</button>
<button onclick="b_js()">import b.js</button>

Because the modules import each other, they cannot be loaded in any order: Pressing either button leads to a "Maximum call stack size exceeded" error.

But if you remove the unnecessary line (which only fills a local variable that is then discarded), it works.

1
Tilak Ved On

You can use alternatively by making functions of a and b. Then call it up to the usage and then the updation of values will be done after callback,

0
özüm On

No, the code is not embedded (copy/pasted) at the imported place. It is executed first. In your example you have a circular reference. The following will ocur (but not exactly, I simplified the concept):

  1. a.js is executed.
  2. a.js tries to import b from b.js.
  3. b.js is executed.
  4. b.js tries to import a from a.js.
  5. a.js is already in memory because of circular reference and not executed again.
  6. b.js continues to next line and tries to log a.
  7. a is not initialized, because a.js is still at line 1 (from the 2 bullet of this list).

There are more than one way to do what you want depending on your needs. The following is one possible example:

a.js

import { logA, b } from "./b.js";

export const a = 2;

logA();
console.log(b);

b.js

import { a } from "./a.js";

export const b = 1;

export function logA() {
  console.log(a);
}