Search code examples
javascriptexecutioncontext

Hosting of let, const


/* Hoisting exampe - let */
let a = 100;
{
	console.log(a); // 100
}
{
	console.log(a); // ReferenceError: a is not defined
	let a = 50;
}

/* Hoisting took place in {}. */
{
	let a=100;
	console.log(a); // 100
}
console.log(a); // ReferenceError: a is not defined

First, I know that let andconst have a block scope.
Compilation happens in Execution context units, and hoisting occurs when LexicalEnvironment is created.
And the execution context is created by the execution of global, function, eval code.

Shouldn't hoisting of const andlet be done in global, function, eval code units?
(It seems that hoisting doesn't seem to happen, but this is purely thanks to the help of TDZ. Internally both const and let hoisting.)

If engine meet block {/* code */} (rather than function) while creating Execution Context, are engine adding a new scope for the block to [[Scope]]?


Solution

  • When a block is first encountered (with the {), a new execution context is created, which creates a new empty Lexical Environment (which is basically a container mapping variable names in the current immediate scope to their values). The engine then iterates through all variables declared with const or let in the immediate block are initialized. (But, despite being initialized, they aren't referenceable until the engine actually comes across the const <variableName> or let <variableName> line - the TDZ)

    So, plain blocks do create a new scope, which will get populated if any statements immediately inside that block declare a variable with const or let.

    You can see this in action with a debugger:

    /* Hoisting took place in {}. */
    {
        debugger;
        let a=100;
        console.log(a); // 100
    }
    console.log(a); // ReferenceError: a is not defined

    Result in Chrome devtools:

    enter image description here

    (though, that's a bit misleading - a doesn't actually contain undefined, it hasn't been fully created at that point)

    The const and let variable names are hoisted in that the interpreter recognizes from the start of the block that it'll be illegal to reference them until they get fully created via the const or let line.

    It's not just plain blocks - any level of code will result in the same sort of thing happening (like at the top level, or inside a for block, or inside a function).