JavaScript Hoisting Explained: How var, let, and const Work

Hoisting in JavaScript

Hoisting is a behavior in which variable and function declarations are moved (or "hoisted") to the top of their containing scope (either the global scope or function scope) before the code is executed. This means that you can use variables and functions before they are declared in the code.

Hoisting in Variables

var

  • Variables declared with var are hoisted to the top of their scope, but their values are not initialized until the point in the code where the assignment occurs.
console.log(x); // undefined
var x = 5;
console.log(x); // 5

let and const

  • Variables declared with let and const are also hoisted but are placed in a "temporal dead zone" until their declarations are reached. Accessing them before they are declared will result in a ReferenceError.
console.log(y); // ReferenceError: Cannot access 'y' before initialization
let y = 10;

// block scope
{
  let x = 5;
}

console.log(x); // ReferenceError: x is not defined

Hoisting in Functions

Traditional Function

  • Function declarations are fully hoisted, meaning both the declaration and the function body are moved to the top of the scope. This allows you to call a function before its declaration in the code.
sayHello(); // "Hello!"
function sayHello() {
  console.log("Hello!");
}
  • In contrast, function expressions (where a function is assigned to a variable) are only hoisted as variables, so calling them before the variable is initialized will result in undefined or a TypeError.
greet(); // TypeError: greet is not a function
var greet = function () {
  console.log("Hi!");
};

greet(); // ReferenceError: Cannot access 'one' before initialization
let greet = function () {
  console.log("Hi!");
};

Arrow Function

  • In contrast, function expressions (where a function is assigned to a variable) are only hoisted as variables, so calling them before the variable is initialized will result in undefined or a TypeError.
greet(); // TypeError: greet is not a function
var greet = () => {
  console.log("Hi!");
};

greet(); // ReferenceError: Cannot access 'one' before initialization
let greet = function () {
  console.log("Hi!");
};

Temporal Dead Zone (TDZ)

The temporal dead zone (TDZ) exists for variables declared using let and const because JavaScript is designed to prevent you from accessing these variables before they are declared and initialized.

Why var and let, const behave differently in hoisting

  • It is because of the historical evolution in JavaScript.

  • Initially, JavaScript was designed for users who are not developers, and the main core usage of JavaScript was to add small interactive elements to the webpage.

  • So var supports only functional scope. Also at that time, there was no block scope.

  • But in the later evolution of JavaScript, it became more complicated to work with var and fix bugs.

  • So to make JavaScript competitive with other modern languages, more features were added like let, const, arrow functions, ES6 methods, etc.

Why var is not updated like let and const

  • It is because of backward compatibility.

  • At that time, JavaScript was widely used by many enterprises, so updating or making changes in existing features would lead to breaking the codebase.

  • Therefore, modern features were added individually.

Common Interview Questions

  • What is hoisting in JavaScript?

  • What is hoisted in JavaScript, and what is not?

  • What’s the difference between var, let, and const respect to hoisting?

  • What is the temporal dead zone (TDZ) in JavaScript?

  • Can you explain hoisting with function declarations vs. function expressions?

  • What is hoisting in ES6 modules?

  • Why should we avoid relying on hoisting in real-world code?

Summary

  • Hoisting is the default behavior in JavaScript where variable and function declarations are moved to the top of their respective scopes during the compilation phase.

  • Hoisting works only for variables declared with var and traditional functions, not for let, const, and arrow functions.

  • Only the function declaration is hoisted, so traditional functions work, but if the function is assigned to a variable, it will not be callable until it is defined.

  • The reason why var and traditional functions are hoisted but let, const, and arrow functions are not because, at the initial stage, JavaScript was mostly used for small UI interactions.

  • But later, as JavaScript was widely used for building applications by enterprises, it became harder to fix bugs with just global scope.

  • So in future updates, more secure concerns will be addressed.

  • Additionally, updating existing features would have broken codebases, so new features were added separately.

1
Subscribe to my newsletter

Read articles from Nishanthan Karunakaran directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Nishanthan Karunakaran
Nishanthan Karunakaran