Search code examples
javascriptscopeinitializationhoistinguncaught-reference-error

Different Uncaught ReferenceError for same situations


I don't understand why for these same situations console returns two different outputs

console.log(a);
let a = 1;

console says a is not defined

and here

function a1() {
    console.log(a);
    let a = 1;
}
a1();

console says Cannot access 'a' before initialization Isn't it supposed to return "Cannot access 'a' before initialization" in both cases? Because we use the let declaration for declaring variables and they are in TDZ in both cases? Why does it say a is not defined?

Why does it work so? Is hoisting of variables in a global scope different from hoisting within block scope?


Solution

  • This is just a consequence of the way the console you used processes what you paste into it. Don't take any lesson from it and apply it outside the console. Console REPLs do several things that are slightly different from the way code really behaves, either at global or function scope (or module scope). Console REPLs are handy for several things, but don't extrapolate what they do to other environments without double-checking.

    In your example the console evaluated each statement without scanning ahead to the next (this is true even if you paste the lines together with line breaks between them).. So first it evaluated console.log(a);, and since there was no declared identifier for a it gave you the standard a is not defined. Then it evaluated your let a = 1;

    But that isn't how code is actually evaluated at global scope. If you have:

    console.log(a);
    let a = 1;
    

    ...the JavaScript engine first scans through all the code looking for declarations like let a (and var a and function and class declarations) and creates bindings (roughly, variables) for them. The bindings for modern declarations (let, const, class) are left uninitialized; old var-style bindings (var, function declarations) are initialized (with undefined or with the function, respectively). So then when it goes to evaluate the step-by-step code, it sees that a is declared, but not initialized, and gives you the Cannot access 'a' before initialization error:

    console.log(a);
    let a = 1;

    It gave you the second behavior when you used a function because the function isn't evaluated until it's complete, and teh evaluation works similarly to the global evaluation I described above, so the JavaScript engine knows that a is declared (but not initialized) and throws the appropriate error.