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