Is ‘Let’ Really Not Hoisted?

Zeeshan Shamsuddeen
The Startup
Published in
3 min readJul 20, 2020

When learning JavaScript, the first thing we hear about JS variables is that var is hoisted, whereas let and const are not hoisted.

But is that really true? Technically NO.

Myth #1

Hoisting: JS engine physically places the variables at the top of the code during execution.

As per MDN: Conceptually, for example, a strict definition of hoisting suggests that variable and function declarations are physically moved to the top of your code, but this is not in fact what happens. Instead, the variable and function declarations are put into memory during the compile phase, but stay exactly where you typed them in your code.

Myth #2

var variables are hoisted. let and const are not.

Technically, all JS variables are hoisted. Where let and cost differs from var in the hoisting process is in the initialization part.

Why do we get ReferenceError when accessing 'let' variables before its declaration? 🤔

Variable Lifecycle

Pre-inform: A JavaScript engine interprets your code in two separate phases. Compilation phase and the Execution phase. Yes. You heard it right. JS does undergo compilation.

Moving on, a JS variable technically has 3 phases.

  1. Declaration phase: Variable is registered to the scope.
  2. Initialization phase: Variable is initialized with undefined.
  3. Assignment phase: Variable is assigned a value.
Pic courtesy: www.dmitripavlutin.com

Note: Please don't confuse the Declaration phase and Variable declaration: Declaration phase is done by the JS compiler and variable declaration is the explicit code written by the user ie. var foo;

Lifecycle of var

  1. Declaration phase: Compile-time.
  2. Initialization phase: Compile-time.
  3. Assignment phase: Execution-time.

For var, the Initialization phase is executed right away after the Declaration phase. And they both are done during compilation.

Pic courtesy: www.dmitripavlutin.com

Comparing this example with the phase diagram,

  1. Declaration phase + Initialization phase: foo is added to the scope and automatically assigned undefined as its value during compilation.
  2. Execution starts.
  3. JS checks for foo in the scope and finds its value as undefined.
  4. Assignment phase: foo is assigned a value of 2.

Lifecycle of let

  1. Declaration phase: Compile-time.
  2. Initialization phase: Execution-time.
  3. Assignment phase: Execution-time.

For let, the Declaration phase is done during compilation. But unlike var, the Initialization phase is not done right after the declaration phase but during the execution time.

At the start of the execution phase, the let variables are declared, but not initialized. These variables are initialized only when the JS engine reaches the let keyword.

Until then, the variable is said to be in the Temporal Dead Zone.

Getting or Setting uninitialized variables that are in the TDZ will result in ReferenceError.

Pic courtesy: www.dmitripavlutin.com

Comparing this example with the phase diagram,

  1. Declaration phase: foo is added to the scope during compilation.
  2. Execution starts. Foo is not initialized at this time and does not have any value, not even undefined. Accessing foo at this point gives ReferenceError.
  3. Initialization phase: When the JS engine encounters the let keyword, foo is assigned undefined.
  4. Assignment phase: foo is assigned a value of 2.

In fact, couple of other statements also behave similarly to let. Eg:const, class

TL;DR

  1. Technically, var, let and const are hoisted.
  2. var is declared and initialized during hoisting.
  3. let and const are only declared during hoisting, not initialized. Accessing uninitialized variables result in ReferenceError.
  4. Preferlet over var, wherever possible to avoid the confusion arising due to hoisting.
  5. Prefer const over let, wherever possible to have strict control over the variables which should not be modified.

--

--