Skip to content
Home JavaScript Scope and Hoisting in JavaScript

Scope and Hoisting in JavaScript

Where developers are forged. · Structured learning · Free forever.
📍 Part of: JS Basics → Topic 11 of 16
JavaScript scope and hoisting explained — var vs let vs const, function scope vs block scope, temporal dead zone, and how hoisting affects function declarations vs expressions.
⚙️ Intermediate — basic JavaScript knowledge assumed
In this tutorial, you'll learn
JavaScript scope and hoisting explained — var vs let vs const, function scope vs block scope, temporal dead zone, and how hoisting affects function declarations vs expressions.
  • var is function-scoped; let and const are block-scoped.
  • Function declarations are fully hoisted — you can call them before they are defined.
  • var is hoisted and initialised to undefined; let/const are hoisted but in the temporal dead zone.
✦ Plain-English analogy ✦ Real code with output ✦ Interview questions
Quick Answer

Scope determines where a variable is accessible. JavaScript has three scope types: global, function (var), and block (let/const). Hoisting moves declarations to the top of their scope before execution — function declarations are fully hoisted, var declarations are hoisted but initialised as undefined, let and const are hoisted but not initialised (temporal dead zone).

var vs let vs const — The Scope Difference

Example · JAVASCRIPT
12345678910111213141516171819202122232425
// var: function-scoped — ignores block boundaries
function testVar() {
  if (true) {
    var x = 10;  // declared inside if block
  }
  console.log(x); // 10 — accessible outside the block!
}
testVar();

// let: block-scoped — respects {} boundaries
function testLet() {
  if (true) {
    let y = 10;
  }
  // console.log(y); // ReferenceError — y not accessible here
}

// const: block-scoped + cannot be reassigned
const MAX = 100;
// MAX = 200; // TypeError: Assignment to constant variable

// const with objects — the binding is const, not the object
const user = { name: 'Alice' };
user.name = 'Bob';  // fine — mutating the object, not the binding
console.log(user.name); // Bob
▶ Output
10
Bob

Hoisting — What Actually Happens

Before executing any code, JavaScript's engine scans for declarations and processes them. Function declarations are fully hoisted. var declarations are hoisted but set to undefined. let and const are hoisted but not accessible until the declaration line.

Example · JAVASCRIPT
123456789101112131415161718192021
// Function declaration — fully hoisted, works before definition
console.log(greet('Forge'));  // 'Hello, Forge!' — works!

function greet(name) {
  return `Hello, ${name}!`;
}

// var — hoisted but undefined
console.log(city);  // undefined — hoisted, not initialised
var city = 'London';
console.log(city);  // 'London'

// let/const — temporal dead zone (TDZ)
// console.log(country);  // ReferenceError: Cannot access before initialization
let country = 'UK';
console.log(country);  // 'UK'

// Function expression — NOT hoisted (it is a variable assignment)
// console.log(sayHi());  // TypeError: sayHi is not a function
const sayHi = function() { return 'Hi!'; };
console.log(sayHi());  // 'Hi!'
▶ Output
Hello, Forge!
undefined
London
UK
Hi!

Lexical Scope and Closures

Example · JAVASCRIPT
123456789101112131415161718192021
const globalVar = 'global';

function outer() {
  const outerVar = 'outer';

  function inner() {
    const innerVar = 'inner';
    // Can access all three — lexical scope chain
    console.log(innerVar);   // 'inner'
    console.log(outerVar);   // 'outer'
    console.log(globalVar);  // 'global'
  }

  inner();
  // console.log(innerVar); // ReferenceError — inner scope not accessible here
}

outer();

// Scope chain: inner → outer → global → undefined
// JS looks up the chain until it finds the variable or exhausts the chain
▶ Output
inner
outer
global

🎯 Key Takeaways

  • var is function-scoped; let and const are block-scoped.
  • Function declarations are fully hoisted — you can call them before they are defined.
  • var is hoisted and initialised to undefined; let/const are hoisted but in the temporal dead zone.
  • The temporal dead zone is the period between hoisting and the actual declaration line.
  • Prefer const by default; use let when you need to reassign; avoid var in modern code.

Interview Questions on This Topic

  • QWhat is the difference between var, let, and const in terms of scope?
  • QWhat is the temporal dead zone?
  • QWhat is the difference between hoisting of function declarations and function expressions?

Frequently Asked Questions

What is the temporal dead zone?

The TDZ is the period from the start of the block scope until the let or const declaration is reached. The variable exists (it has been hoisted) but cannot be accessed — reading it throws ReferenceError. This is by design to prevent bugs that var's hoisting allows.

Why does var inside a for loop leak out of the loop?

Because var is function-scoped, not block-scoped. The for loop's curly braces do not create a new scope for var. The variable lives in the containing function. This is the same reason the loop closure trap happens. Use let in for loops to get a new binding per iteration.

🔥
Naren Founder & Author

Developer and founder of TheCodeForge. I built this site because I was tired of tutorials that explain what to type without explaining why it works. Every article here is written to make concepts actually click.

← PreviousType Coercion in JavaScriptNext →this Keyword in JavaScript
Forged with 🔥 at TheCodeForge.io — Where Developers Are Forged