Destructuring Tweets β Episode 2 β Hoisting
Let's destructure a social media code quiz about hoisting in this second episode of my series. π
The Snippet
function fooBar() {
try {
console.log(foo);
} catch (error) {
console.log(error.name);
}
try {
console.log(bar);
} catch (error) {
console.log(error);
}
var foo = 'hello';
let bar = 'world';
}
fooBar();
In this snippet, they start with two try
/catch
blocks. These catch errors and allow us to act upon them, like adding an entry to our log database or informing the user.
Both print a variable or the name of the thrown object in case of an error. Note that both of the trying-to-be-printed variables are yet to be declared. That missing is the core trickery here.
After the two try
/catch
blocks, we have the actual declaration of the variables. The first is initialized via var
, and the second with let
.
The Output
So, what will the output be if I run the given function? Surprisingly enough, it's undefined
and a ReferenceError
. To be a little more precise, we print the variable foo
(which is undefined
at this point) but not the variable bar
. Latter is recognized as not declared at all, hence ReferenceError
, which semantically means βYou did not declare this variableβ.
The Analysis
First things first, why is foo
undefined
? Shouldn't it be hello
? No, because of a concept called hoisting. Javascript engines move the (non-lexical) variable declarations to the top of the scope! What does this mean for our example? This shows how foo
gets processed:
function fooBar() {
var foo; // undefined
try {
console.log(foo);
} catch (error) {
console.log(error.name);
}
foo = 'hello';
}
An uninitialized variable is always just undefined
. The variable is declared; hence, it can be printed without an assigned value.
The second and more significant question is why the behavior differs for let
and var
. Easy answer: let
is a lexical variable, while var
is not. ES6 introduced the difference for precisely this type of mistake. The interpreter is more prone to detect hoisting issues that way.
A lexical variable behaves like most of us would intuitively expect it to. One can not access it before it gets initialized. Such are placed in the Temporal Dead Zone (TDZ). Notably, lexical variables, so practically let
and const
, do not get hoisted.
βΉοΈ One addition, one might instantly think that this snippet wants to trick you with scope differences. That's not the case here! The block scope equals the function scope.
Further Reading
β https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Cant_access_lexical_declaration_before_init
β https://hacks.mozilla.org/2015/07/es6-in-depth-let-and-const/
β https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let#temporal_dead_zone_tdz
β https://developer.mozilla.org/en-US/docs/Glossary/Hoisting