Blockless Scope: JavaScript Shenanigans
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 withletare scoped to theforstatement 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
varis function‑scoped (or global‑scoped) and can be redeclared without error.let(andconst) are lexically scoped. In aforloop, 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.