function b() {
function a() {
console.log(x);
}
a();
const x = 10;
}
const x = 20;
b()
If I understand lexical scoping and execution context correctly, when function a() is invoked, it should have a reference to b's lexical environment. During the execution of a(), the x const inside of b() is inside of the temporal dead zone until a finishes executing. However, shouldn't a() recognize that there is no x value to access (since it's in the TDZ) and thus should it not be searching through the global lexical environment to find that const x = 20?
in the above case, a() throws a reference error.
To explain what's happening internally, when
b()is called a new environment record (ER) is created. Conceptually, the "ER" represents a "scope" and is responsible for holding the bindings (variables) which are created within thebfunction. The ER can have a binding created within it but no value associated with it, so a binding can be instantiated but not initialised to a value (due to this, the binding is in the TDZ). In the case ofb, an unitialised binding forxis created withinb's ER before any of the code withinbhas actually run. It is only once the code withinb()starts running and we reach the lineconst x = 10;does thexbinding become initialized with a value. That means that even though you're callinga()before the lineconst x = 10;is evaluated, thexbinding has already been created, just without a value (this is whyxis "hoisted")When you try and access
xfrom withina,a's scope (ie: its ER) is first checked forx. Asxisn't defined withina, we walk up the scope chain to the surrounding scope to search that forx. As the surrounding scope isb's scope,b's ER is checked for thexbinding, which as explained above, it has. That's why we stop searching the scope chain when we checkb's scope, as we've already found a binding forx, and thus we never check the global scope for itsxbinding. However, as the binding forxthat we found withinb's ER has no value and is uninitialised, JavaScript will throw a ReferenceError.