Blockless Scope: JavaScript Shenanigans

Published: (May 27, 2026 at 07:29 PM EDT)
3 min read
Source: Dev.to

Source: Dev.to

var

For var, nothing changes. It uses only function or global scope. Using var in the for declaration makes the variable available to the containing function (or the global scope), and the value persists after the loop completes.

var a = 1;

for (var a = 2; a  console.log(i), 10);

// logs 0‑9
for (let i = 0; i  console.log(i), 10);

MDN – For statement initialization
Variables declared with let are scoped to the for statement itself, not to the surrounding block.

When we talk about “block scope” in ES2015+, we’re really referring to lexical scope—the scope determined by where a variable is written in the source code. let creates a lexical scope that is exclusive to the loop statement, even if the loop body isn’t a block.


Function‑scope example

You can create arbitrary blocks at any time; each block gets its own scope.

function scope(a = 2) {
  console.log(a);          // 2

  // Redeclaring the variable in the function scope
  var a = 'a';
  console.log(a);          // 'a'

  // var redeclaration inside the function scope
  for (var a = 8; a < 10; a += 1) console.log(a); // 8, 9
  console.log(a);          // 10 (var is function‑scoped)

  // let creates a new block scope for the loop
  for (let a = 1; a < 3; a += 1) console.log(a); // 1, 2
  console.log(a);          // 10 (back to function‑scoped `a`)

  {
    // Free‑floating block with its own scope
    let a = 'random block';
    console.log(a);        // 'random block'
  }

  console.log(a);          // 10 (back to function scope)

  // Loop with mixed declarations
  for (a = 1; a < 5; a += 1) {
    var b = 2;   // function‑scoped, hoisted
    let c = 3;   // block‑scoped
    console.log(a, b, c);
  }

  console.log(b);           // 2 (still accessible)

  try {
    console.log(c);        // ReferenceError
  } catch (e) {
    // `d` is scoped to the catch block, `e` is the caught error binding
    let d = 4;
    console.log(d, e);
  }
}

scope();

Output (illustrative):

2
a
8
9
10
1
2
10
random block
10
1 2 3
2 2 3
3 2 3
4 2 3
2
4 ReferenceError: c is not defined

Global scope

Variables declared with var inside a function are not added to the global scope. Attempting to access them globally results in a ReferenceError.

// ReferenceError – not declared in the global scope
console.log(a);

Summary

  • var is function‑scoped (or global‑scoped) and can be redeclared without error.
  • let (and const) are lexically scoped. In a for loop, the initialization expression gets its own scope that lives for the duration of the loop statement, even when the loop body isn’t a block.
  • Arbitrary blocks ({ … }) can be introduced anywhere to create additional lexical scopes.
  • Function arguments are bindings that behave like variables within the function’s lexical scope.

These nuances explain why let appears to have “block scope” even when the loop body lacks explicit braces.

0 views
Back to Blog

Related posts

Read more »