Block Scoping

What is Block Scoping?

Block scoping means variables are limited to the block {} in which they are declared. let and const provide block scope, while var provides function scope.

Try Block Scoping

Code Editor
Console Output
Click "Run" to execute your code...

Example 1: Basic Block Scope

// Block scope with let and const
{
  let blockLet = 'I am block scoped';
  const blockConst = 'Me too';
  var blockVar = 'I leak out!';
  
  console.log(blockLet);   // I am block scoped
  console.log(blockConst); // Me too
  console.log(blockVar);   // I leak out!
}

// console.log(blockLet);   // ReferenceError
// console.log(blockConst); // ReferenceError
console.log(blockVar);      // I leak out! (var is not block-scoped)

// Multiple blocks
{
  let x = 1;
  console.log(x); // 1
}
{
  let x = 2; // Different variable!
  console.log(x); // 2
}

Example 2: Block Scope in if Statements

let message = 'outer';

if (true) {
  let message = 'inner'; // New variable, shadows outer
  console.log(message); // inner
}

console.log(message); // outer

// With var (no block scope)
var count = 10;

if (true) {
  var count = 20; // Same variable!
  console.log(count); // 20
}

console.log(count); // 20 (modified by if block)

// Practical example
function checkAge(age) {
  if (age >= 18) {
    let status = 'adult';
    console.log(status); // adult
  }
  // console.log(status); // ReferenceError
  
  if (age >= 18) {
    var statusVar = 'adult var';
    console.log(statusVar); // adult var
  }
  console.log(statusVar); // adult var (var leaks out!)
}

checkAge(20);

Example 3: Block Scope in Loops

// Classic var problem in loops
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log('var:', i), 100);
}
// Output: var: 3, var: 3, var: 3

// Fixed with let (block scope)
for (let j = 0; j < 3; j++) {
  setTimeout(() => console.log('let:', j), 100);
}
// Output: let: 0, let: 1, let: 2

// Each iteration gets its own 'j'
for (let k = 0; k < 3; k++) {
  const multiplied = k * 2;
  console.log(multiplied); // 0, 2, 4
}
// console.log(k); // ReferenceError
// console.log(multiplied); // ReferenceError

// Array iteration
const numbers = [1, 2, 3, 4, 5];
for (let num of numbers) {
  const squared = num * num;
  console.log(squared); // 1, 4, 9, 16, 25
}
// console.log(num); // ReferenceError
// console.log(squared); // ReferenceError

Example 4: Block Scope in switch Statements

function getDayMessage(day) {
  switch(day) {
    case 'Monday': {
      let message = 'Start of week';
      console.log(message);
      break;
    }
    case 'Friday': {
      let message = 'End of week'; // Different variable!
      console.log(message);
      break;
    }
    default: {
      let message = 'Middle of week';
      console.log(message);
    }
  }
  // console.log(message); // ReferenceError
}

getDayMessage('Monday');   // Start of week
getDayMessage('Friday');   // End of week
getDayMessage('Wednesday'); // Middle of week

// Without block scope (var problem)
function getDayMessageVar(day) {
  switch(day) {
    case 'Monday':
      var messageVar = 'Start of week';
      console.log(messageVar);
      break;
    case 'Friday':
      // var messageVar = 'End of week'; // SyntaxError: already declared
      messageVar = 'End of week';
      console.log(messageVar);
      break;
  }
  console.log(messageVar); // Accessible outside switch!
}

Example 5: Practical Block Scoping Patterns

// Pattern 1: Temporary variables
function processArray(arr) {
  const result = [];
  
  for (let i = 0; i < arr.length; i++) {
    // Temporary calculation in block scope
    {
      const doubled = arr[i] * 2;
      const squared = doubled * doubled;
      result.push(squared);
    }
    // doubled and squared are not accessible here
  }
  
  return result;
}

console.log(processArray([1, 2, 3])); // [4, 16, 36]

// Pattern 2: Configuration blocks
function setupEnvironment(env) {
  if (env === 'development') {
    const API_URL = 'http://localhost:3000';
    const DEBUG = true;
    console.log('Dev mode:', API_URL, DEBUG);
  } else if (env === 'production') {
    const API_URL = 'https://api.example.com';
    const DEBUG = false;
    console.log('Prod mode:', API_URL, DEBUG);
  }
  // API_URL and DEBUG are not accessible here
}

setupEnvironment('development');

// Pattern 3: Error handling with block scope
function parseData(data) {
  try {
    const parsed = JSON.parse(data);
    const validated = validate(parsed);
    return validated;
  } catch (error) {
    const message = `Parsing failed: ${error.message}`;
    console.error(message);
    return null;
  }
  // parsed, validated, error, message not accessible here
  
  function validate(obj) {
    return obj && typeof obj === 'object' ? obj : null;
  }
}

parseData('{"name": "John"}');

Key Points

  • let and const are block-scoped (limited to {} blocks)
  • var is function-scoped, not block-scoped
  • Block scope applies to: if, for, while, switch, try-catch, and standalone blocks
  • Each loop iteration with let creates a new binding
  • Block scoping helps prevent variable leakage and naming conflicts
  • Use const by default, let when reassignment is needed, avoid var