JavaScript Code Blocks
A code block is a section of code enclosed in curly braces {}. Code blocks define scope boundaries for let and const (but not var). Understanding how blocks work with different variable declarations is fundamental to writing bug-free JavaScript.
Block Statements
A block statement groups zero or more statements inside curly braces {}. Blocks are used in if/else, for, while, switch, try/catch, and can also stand alone. With let and const, a block creates a new scope.
// Standalone block — creates scope for let/const
{
let x = 10;
const y = 20;
console.log("Inside block:", x, y);
}
// x and y are NOT accessible here
// console.log(x); // ReferenceError
// Nested blocks
{
let outer = "outer";
{
let inner = "inner";
console.log(outer);
console.log(inner);
}
console.log(outer);
// console.log(inner); // ReferenceError
}Block Scope with let and const
Variables declared with let and const are block-scoped. They only exist within the block where they are declared. This is the expected behavior in most programming languages and prevents accidental variable leakage.
let a = "global a";
if (true) {
let a = "block a";
let b = "block b";
console.log(a);
console.log(b);
}
console.log(a);
// console.log(b); // ReferenceError
// const works the same way
const PI = 3.14;
if (true) {
const PI = 3.14159;
console.log("Inner PI:", PI);
}
console.log("Outer PI:", PI);var Leaks Out of Blocks
Unlike let and const, var does NOT respect block scope. Variables declared with var are scoped to the nearest function (or global scope). They 'leak' out of if blocks, for loops, and other code blocks.
if (true) {
var leaked = "I leaked out!";
let contained = "I stayed inside";
}
console.log(leaked);
// console.log(contained); // ReferenceError
// var in a for loop leaks
for (var i = 0; i < 3; i++) {
// loop body
}
console.log("var i after loop:", i);
// let in a for loop is contained
for (let j = 0; j < 3; j++) {
// loop body
}
// console.log(j); // ReferenceErrorFunction Blocks
Function bodies are blocks that create scope for all variable types (var, let, and const). This is the only type of block that constrains var. Each function call creates its own scope.
function example() {
var funcVar = "I'm function-scoped";
let funcLet = "I'm also function-scoped";
const funcConst = "Me too!";
console.log(funcVar);
console.log(funcLet);
console.log(funcConst);
}
example();
// None of these are accessible outside
// console.log(funcVar); // ReferenceError
// console.log(funcLet); // ReferenceError
// console.log(funcConst); // ReferenceError
// Each call has its own scope
function counter() {
let count = 0;
count++;
console.log(count);
}
counter();
counter();Loop and Conditional Blocks
Loop blocks (for, while, do...while) and conditional blocks (if, else, switch) create new block scopes for let and const. This is especially important in loops where closures capture variables.
// Classic problem with var in loops
var funcsVar = [];
for (var i = 0; i < 3; i++) {
funcsVar.push(function() { return i; });
}
// All functions share the same 'i' (which is 3)
console.log(funcsVar[0]());
console.log(funcsVar[1]());
console.log(funcsVar[2]());
// Fixed with let — each iteration gets its own 'i'
let funcsLet = [];
for (let i = 0; i < 3; i++) {
funcsLet.push(function() { return i; });
}
console.log(funcsLet[0]());
console.log(funcsLet[1]());
console.log(funcsLet[2]());
// Switch block
let val = 2;
switch (val) {
case 1: {
let msg = "one";
console.log(msg);
break;
}
case 2: {
let msg = "two";
console.log(msg);
break;
}
}| Declaration | Block Scope | Function Scope | Global Scope |
|---|---|---|---|
| var | No (leaks out) | Yes | Yes |
| let | Yes | Yes | Yes (no window prop) |
| const | Yes | Yes | Yes (no window prop) |
| function | Varies by mode | Yes | Yes |