Understanding Hoisting in JavaScript: The Secrets Behind var, let, and const

Understanding Hoisting in JavaScript: The Secrets Behind var, let, and const

Hoisting is a fundamental concept in JavaScript that refers to how variable and function declarations are moved to the top of their containing scope during the compilation phase, before the code execution starts. This behavior can sometimes be tricky to understand, especially when working with different types of variable declarations like var, let, and const. In this blog post, we will break down how hoisting works and why accessing var before its declaration works differently than accessing let and const.

Hoisting with var: Accessible Before Declaration, but Undefined

In JavaScript, var declarations are hoisted to the top of their scope, but their initialization (assignment of value) is not. This means that even if you attempt to log the value of a var variable before it's assigned a value, it will not result in an error, but instead, you’ll see undefined in the console.

Example:

console.log(myVar); // undefined
var myVar = 10;
console.log(myVar); // 10

What just happened?

  • The declaration of myVar (var myVar) is hoisted to the top of the code block or function scope. [not really]

  • The value 10 is not assigned to myVar until the line where myVar = 10; is encountered.

  • Therefore, when you try to log myVar before the assignment, it’s undefined, as the declaration is hoisted but not the assignment.

But what is really happening behind the scenes?

The myVar variable is already allocated memory during the memory phase in the execution context, even before the code runs, and the memory allocated is set to undefined. Therefore, when we try to log myVar before it's declared, we don't get an error; instead, we see undefined.

Hoisting with let and const: The Temporal Dead Zone

While var behaves a certain way during hoisting, the behavior of let and const is different. Both let and const are hoisted to the top of their block or global scope just like var. However, they enter what is known as the Temporal Dead Zone (TDZ) from the start of the block until they are initialized.

If you try to access a let or const variable before its declaration, JavaScript will throw a ReferenceError, indicating that the variable is in the Temporal Dead Zone. This is because, although the variables are hoisted, they cannot be accessed until they are initialized.

Example:

console.log(myLet); // ReferenceError: Cannot access 'myLet' before initialization
let myLet = 20;
console.log(myConst); // ReferenceError: Cannot access 'myConst' before initialization
const myConst = 30;

What’s happening?

  • let and const declarations are hoisted, but they don’t get initialized until their respective lines of declaration are executed.

  • Until that happens, any attempt to access them results in a ReferenceError because of the Temporal Dead Zone.

  • The Temporal Dead Zone ensures that you don’t accidentally access a variable before it has been fully declared and initialized.

The Scope of Variables: Block vs. Global

Even though var, let, and const behave differently during hoisting, they all still exist in their respective scopes — either block scope or global scope.

  • var is function-scoped (or globally scoped if declared outside a function).

  • let and const are block-scoped, meaning they are only accessible within the block (e.g., inside curly braces {}) where they are declared.

Example:

javascriptCopy codeif (true) {
  var x = 5; // Function or global scoped
  let y = 10; // Block scoped
}

console.log(x); // 5 (accessible)
console.log(y); // ReferenceError: y is not defined (block-scoped)

What’s happening?

  • The var declaration is accessible even outside the block because var is function-scoped or globally scoped.

  • The let variable is block-scoped, so it cannot be accessed outside the block in which it is declared.

Why is it called Hoisting?

The term hoisting can be a bit misleading, as it suggests that the variables or functions are literally moved to the top of their scope, which isn't quite what happens under the hood.

The term comes from the way JavaScript behaves during its compilation phase. While the declaration of variables and functions is processed at the top of the execution context (the scope), it's not like the code itself is physically "moved" to the top. Instead, JavaScript prepares an environment where all the variable and function declarations are set up before any code execution begins.

In this setup, the "hoisting" metaphor refers to the process of raising or moving the declarations to the top during the compilation phase, even though the actual code remains in its original order. So, when we talk about hoisting, we are really referring to how the JavaScript engine processes declarations before running the code.

Conclusion: Hoisting and Scoping Rules in a Nutshell

  1. var: Hoisted to the top, but only the declaration, not the assignment. This means you can access the variable before its declaration, but it will be undefined until the assignment is executed.

  2. let and const: Also hoisted, but they enter the Temporal Dead Zone, meaning you cannot access them before their declaration and initialization. This ensures safer code by preventing premature access.

  3. Scope: The scope of var is function or global, while let and const are block-scoped.

Understanding hoisting and how it affects different variable declarations is crucial for writing clean, bug-free JavaScript code. By recognizing when and where variables are hoisted and how their scopes work, you can avoid common pitfalls and improve the reliability of your code.