Skip to content

Block Scope

This lesson explains Block Scope with clear examples, use cases, and best practices so you can use modern JavaScript confidently in real projects.

ES6 Block Scope Overview

ES6 introduced block scope using let and const. Block scope means a variable is available only inside the nearest pair of curly braces where it is declared, such as an if block, for loop, while loop, or standalone block.

Block scope helps prevent accidental variable leaks, duplicate variable names, loop bugs, hoisting confusion, and unexpected changes in large JavaScript applications.

Keyword Scope Can Reassign? Can Redeclare in Same Scope?
var Function scope Yes Yes
let Block scope Yes No
const Block scope No No

What Is a Block?

A block is any code wrapped inside curly braces. Variables declared with let and const are limited to that block.

if (true) {
  const message =
    "Inside block";

  console.log(message);
}

// message is not available here

The variable message exists only inside the if block.

let Block Scope

if (true) {
  let status =
    "active";

  console.log(status);
}

// ReferenceError

console.log(status);

Variables declared with let cannot be accessed outside the block where they are created.

const Block Scope

if (true) {
  const role =
    "admin";

  console.log(role);
}

// ReferenceError

console.log(role);

Like let, const is also block-scoped.

var Is Not Block Scoped

if (true) {
  var message =
    "Hello";
}

console.log(message);
// Hello

The var keyword ignores block scope and is available outside the if block. This can create bugs in larger programs.

Function Scope with var

function showMessage() {
  if (true) {
    var message =
      "Inside function";
  }

  console.log(message);
}

showMessage();
// Inside function

var is scoped to the function, not the block.

Block Scope in for Loops

for (let index = 0; index < 3; index++) {
  console.log(index);
}

// ReferenceError

console.log(index);

The index variable exists only inside the loop block.

Classic var Loop Problem

for (var i = 1; i <= 3; i++) {
  setTimeout(() => {
    console.log(i);
  }, 1000);
}

// 4
// 4
// 4

Because var is function-scoped, all callbacks share the same variable.

let Fixes Loop Scope

for (let i = 1; i <= 3; i++) {
  setTimeout(() => {
    console.log(i);
  }, 1000);
}

// 1
// 2
// 3

With let, each loop iteration gets its own block-scoped variable.

Redeclaration Rules

let count =
  1;

// SyntaxError

let count =
  2;
var total =
  100;

var total =
  200;

console.log(total);
// 200

let and const do not allow redeclaration in the same scope, which helps catch mistakes early.

Reassignment Rules

let score =
  10;

score =
  20;

console.log(score);
// 20
const maxScore =
  100;

// TypeError

maxScore =
  200;

Use let when a value must change. Use const when the variable should not be reassigned.

const with Objects

const user =
  {
    name: "Alice",
    active: true
  };

user.active =
  false;

console.log(user);

const prevents reassignment of the variable reference, but it does not freeze object properties.

const with Arrays

const skills =
  ["HTML", "CSS"];

skills.push("JavaScript");

console.log(skills);

Arrays declared with const can still be modified. Only reassignment is blocked.

Temporal Dead Zone

Variables declared with let and const are in the temporal dead zone from the start of the block until the declaration is reached.

console.log(name);

// ReferenceError

let name =
  "John";

This prevents using variables before they are declared.

Hoisting Difference

console.log(message);

var message =
  "Hello";

// undefined
console.log(status);

// ReferenceError

let status =
  "active";

var is hoisted and initialized as undefined. let and const are hoisted but not initialized until execution reaches the declaration.

Nested Block Scope

const role =
  "user";

if (true) {
  const role =
    "admin";

  console.log(role);
  // admin
}

console.log(role);
// user

Inner blocks can have variables with the same name without changing the outer variable.

Standalone Block Scope

{
  const temporaryValue =
    "Only inside this block";

  console.log(temporaryValue);
}

// ReferenceError

console.log(temporaryValue);

Standalone blocks can be used to limit temporary variables to a small section of code.

Block Scope in try catch

try {
  const response =
    "Success";

  console.log(response);
} catch (error) {
  console.error(error.message);
}

// ReferenceError

console.log(response);

Variables declared inside try or catch blocks are not available outside those blocks.

Block Scope in switch Statements

const status =
  "success";

switch (status) {
  case "success": {
    const message =
      "Saved successfully";

    console.log(message);
    break;
  }

  case "error": {
    const message =
      "Save failed";

    console.log(message);
    break;
  }
}

Wrapping each case in braces helps avoid duplicate variable declaration errors.

Global Scope Difference

var oldValue =
  "var global";

let modernValue =
  "let global";

console.log(window.oldValue);
// var global

console.log(window.modernValue);
// undefined

Global var declarations become properties of window in browsers. Global let and const do not.

Real-World Examples

Form Validation

function validateForm(user) {
  if (!user.email) {
    const error =
      "Email is required";

    return error;
  }

  return "Valid";
}

API Response Handling

async function loadUsers() {
  const response =
    await fetch("/api/users");

  if (response.ok) {
    const users =
      await response.json();

    return users;
  }

  return [];
}

Loop Event Handlers

const buttons =
  document.querySelectorAll("button");

for (let index = 0; index < buttons.length; index++) {
  buttons[index].addEventListener("click", () => {
    console.log("Button", index);
  });
}

var vs let vs const

Feature var let const
Scope Function Block Block
Reassignment Allowed Allowed Not Allowed
Redeclaration Allowed Not allowed in same scope Not allowed in same scope
Hoisting Behavior Hoisted as undefined Temporal dead zone Temporal dead zone
Global window Property Yes in browsers No No
Best Use Avoid in modern code Changing values Default choice

Common Block Scope Use Cases

  • Loop counters with let.
  • Constants with const.
  • Temporary variables inside if blocks.
  • Scoped variables inside try catch.
  • Scoped variables inside switch cases.
  • Preventing duplicate variable names in nested code.
  • Improving safety in event handlers.
  • Reducing accidental global variable leaks.

Common Block Scope Mistakes

  • Using var inside blocks and expecting block scope.
  • Trying to access let or const outside their block.
  • Using variables before declaration and hitting the temporal dead zone.
  • Redeclaring let or const in the same scope.
  • Thinking const freezes objects or arrays.
  • Forgetting braces around switch cases with duplicate names.
  • Using let when const would be safer.
  • Expecting global let variables to appear on window.

Block Scope Best Practices

  • Use const by default.
  • Use let only when reassignment is required.
  • Avoid var in modern JavaScript.
  • Declare variables close to where they are used.
  • Keep blocks small and readable.
  • Use braces around switch cases when declaring variables.
  • Use block scope to limit temporary variables.
  • Do not access variables before declaration.

Key Takeaways

  • Block scope means variables are limited to curly braces.
  • let and const are block-scoped.
  • var is function-scoped, not block-scoped.
  • const prevents reassignment, not object mutation.
  • The temporal dead zone prevents using variables before declaration.
  • Use const first, let when needed, and avoid var.

Pro Tip

In interviews, explain block scope with one simple sentence: let and const live only inside the nearest curly braces, while var lives inside the nearest function.