skip to Main Content

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

Answers


  1. 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.

    Login or Signup to reply.
  2. 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,

    Login or Signup to reply.
  3. 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);
    }
    
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search