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.