Last modified: Jul 17, 2026

JavaScript Block vs Function Scope

Understanding scope is essential for writing clean JavaScript code.

Scope determines where variables are accessible in your program.

Two main types are block scope and function scope.

They control variable visibility differently.

This article explains both with simple examples.

You will learn when to use each one.

We also cover common mistakes to avoid.

What Is Function Scope?

Function scope means a variable is only available inside a function.

Variables declared with var have function scope.

They are visible anywhere inside the function.

Even before they are declared due to hoisting.

But they are not accessible outside the function.

Here is an example:

 
function greet() {
  var message = "Hello!";
  console.log(message); // Output: Hello!
}
greet();
// console.log(message); // Error: message is not defined

message is only available inside greet().

Accessing it outside causes a reference error.

This is function scope in action.

Hoisting with Function Scope

Variables declared with var are hoisted.

Hoisting means the declaration moves to the top of the function.

The variable is initialized with undefined.

This can lead to unexpected behavior.

Example:

 
function test() {
  console.log(x); // Output: undefined (not error)
  var x = 10;
  console.log(x); // Output: 10
}
test();

The first console.log() does not throw an error.

It prints undefined because x is hoisted but not assigned.

This is a key feature of function scope with var.

What Is Block Scope?

Block scope restricts variable access to a block.

A block is any code between curly braces {}.

Examples include if, for, while, or standalone blocks.

Variables declared with let and const have block scope.

They are only visible inside that block.

Example:

 
if (true) {
  let secret = "hidden";
  console.log(secret); // Output: hidden
}
// console.log(secret); // Error: secret is not defined

secret is only available inside the if block.

Outside the block, it does not exist.

This prevents accidental variable leakage.

No Hoisting to the Block

Variables with let and const are not initialized until the declaration.

They exist in a "temporal dead zone" from the start of the block.

Accessing them before declaration causes a ReferenceError.

Example:

 
{
  // console.log(y); // Error: Cannot access 'y' before initialization
  let y = 20;
  console.log(y); // Output: 20
}

This is safer than function scope because errors appear early.

You cannot accidentally use an uninitialized variable.

Key Differences Between Block and Function Scope

Here are the main differences:

  • Declaration keywords:var uses function scope; let and const use block scope.
  • Hoisting behavior:var hoists with undefined; let and const hoist but are not initialized.
  • Visibility: Function scope spans the whole function; block scope is limited to the block.
  • Re-declaration:var allows re-declaration in the same scope; let and const do not.

Choosing the right scope improves code clarity.

Modern JavaScript prefers block scope for most cases.

Practical Examples

Let us see both scopes in loops.

Using var in a loop:

 
for (var i = 0; i < 3; i++) {
  setTimeout(function() {
    console.log(i); // Output: 3, 3, 3 (after loop ends)
  }, 100);
}
console.log(i); // Output: 3 (accessible outside loop)

i is function-scoped to the global or enclosing function.

All callbacks share the same i which ends at 3.

This is a common bug.

Using let in a loop:

 
for (let j = 0; j < 3; j++) {
  setTimeout(function() {
    console.log(j); // Output: 0, 1, 2 (each callback gets its own j)
  }, 100);
}
// console.log(j); // Error: j is not defined

j is block-scoped to each iteration.

Each callback captures a different value.

This is the expected behavior.

Common Mistakes

Beginners often confuse scope types.

One mistake is using var inside a block expecting block scope.

Example:

 
if (true) {
  var value = "I leak out!";
}
console.log(value); // Output: "I leak out!" (unexpected)

Because var is function-scoped, value leaks outside the block.

Use let or const to prevent this.

Another mistake is forgetting the temporal dead zone.

Accessing a let variable before declaration causes an error.

Always declare variables at the top of their block.

Best Practices

Follow these guidelines:

  • Use const by default for values that do not change.
  • Use let when you need to reassign a variable.
  • Avoid var in modern code unless you have a specific reason.
  • Limit variable scope to the smallest possible block.
  • Understand JavaScript Variable Scope Explained for deeper insights.

This reduces bugs and improves readability.

Relationship with Variable Shadowing

Scope affects variable shadowing.

Shadowing happens when an inner scope declares a variable with the same name as an outer scope.

Block scope allows precise shadowing.

Example:

 
let x = 10;
{
  let x = 20; // shadows outer x
  console.log(x); // Output: 20
}
console.log(x); // Output: 10

Learn more about JavaScript Variable Shadowing to master this concept.

Declaration Rules and Types

Each scope type has its own declaration rules.

For example, you cannot declare a let variable twice in the same block.

But you can in different blocks.

Review the JavaScript Variable Declaration Guide for complete details.

Also, check the Types of JavaScript Variables for more context.

Conclusion

Block scope and function scope are fundamental concepts.

Function scope uses var and hoists variables to the function.

Block scope uses let and const and limits visibility to a block.

Modern JavaScript favors block scope for safer code.

Always choose the smallest scope for your variables.

This prevents bugs and makes your code easier to understand.

Practice with examples to solidify your knowledge.

Now you can confidently write scoped variables in JavaScript.