Sitemap
Rohit Kuwar

Rohit Kuwar

Senior Software Engineer

Understanding Scope in JavaScript: A Complete Guide

4 min readJul 24, 2025

--

Scope in JavaScript

When writing JavaScript, one of the most fundamental — and often misunderstood — concepts is scope. Understanding how scope works in JavaScript is crucial for writing predictable, bug-free code.

In this blog, we’ll explore:

  • What scope means in JavaScript
  • The different types of scope
  • How var, let, and const interact with scope
  • Practical examples to clarify the behavior

🧠 What is Scope?

In JavaScript, scope refers to the current context of execution in which values and expressions are visible or accessible. In simple terms, scope determines where variables can be accessed or referenced in your code.

There are three primary types of scope in JavaScript:

  1. Global Scope
  2. Function (Local) Scope
  3. Block Scope

We’ll go through each in detail.

🌍 1. Global Scope

Variables declared outside of any function or block belong to the global scope. These are accessible anywhere in your code after they are declared.

var name = "JavaScript";

function greet() {
console.log(name); // Accessible here
}

greet(); // Output: JavaScript
console.log(name); // Output: JavaScript

🔥 Tip: In the browser environment, global variables become properties of the window object.

🔁 2. Function Scope

Variables declared inside a function using var, let, or const are scoped to that function. This means they cannot be accessed from outside the function.

function sayHello() {
var message = "Hello!";
console.log(message); // Accessible here
}

sayHello();
console.log(message); // ❌ ReferenceError: message is not defined

Function scope is one of the most common places developers encounter scoping issues, especially when using var.

📦 3. Block Scope

Introduced with ES6, block scope applies to variables declared with let and const. These variables are only accessible within the block { ... } in which they are defined.

{
let x = 10;
const y = 20;
console.log(x, y); // Output: 10 20
}

console.log(x, y); // ❌ ReferenceError: x is not defined

On the other hand, var does not obey block scope.

{
var z = 30;
}

console.log(z); // ✅ Output: 30

🧱 4. Module Scope

With the introduction of ES6 modules, JavaScript gained module scope, where variables, functions, and classes declared in a module are scoped only to that module and are not accessible globally unless explicitly exported.

// utils.js
const secret = "hidden";
export const publicValue = 42;

// app.js
import { publicValue } from './utils.js';

console.log(publicValue); // ✅ 42
console.log(secret); // ❌ ReferenceError: secret is not defined

📌 Key Points:

  • Every file using import or export is treated as a module.
  • Variables and functions declared in a module are not added to the global scope.
  • To share functionality between modules, you must export/import explicitly.
  • Modules also run in strict mode by default.

🔐 Module scope helps you write better-encapsulated, modular code and avoid naming collisions in larger codebases.

🆚 var vs let vs const

Here’s a quick comparison of how these three keywords behave:

Feature           | var             | let              | const
------------------|------------------|------------------|------------------
Scope | Function scope | Block scope | Block scope
Hoisting | Yes (undefined) | Yes (TDZ) | Yes (TDZ)
Re-declaration | Allowed | Not allowed | Not allowed
Re-assignment | Allowed | Allowed | ❌ Not allowed

TDZ = Temporal Dead Zone

Note: While all three are hoisted, let and const remain in the temporal dead zone until they're declared, so accessing them before the declaration throws a ReferenceError.

Hoisting Example:

console.log(a); // undefined
var a = 5;

console.log(b); // ❌ ReferenceError
let b = 10;

With var, the declaration is hoisted to the top of the function or global scope, but the value remains undefined until assignment. let and const are hoisted too, but in the temporal dead zone (TDZ) until their declaration is evaluated.

🧪 Real-World Example:

function count() {
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 1000);
}
}
count();
// Output after 1 second: 3 3 3

Why? Because var is function-scoped. By the time the setTimeout callback executes, the loop has already completed and i is 3.

Get Rohit Kuwar’s stories in your inbox

Join Medium for free to get updates from this writer.

Now let’s use let:

function count() {
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 1000);
}
}
count();
// Output after 1 second: 0 1 2

This happens because let creates a new binding for i in each loop iteration.

🧩 Lexical Scope

JavaScript uses lexical scoping, which means that the scope of a variable is determined by its position in the source code.

function outer() {
let outerVar = "I’m outside!";

function inner() {
console.log(outerVar); // ✅ Has access to outer scope
}

inner();
}

outer();

Here, inner() has access to variables defined in outer() due to lexical scope.

✅ Best Practices:

  • Use let and const over var—they are more predictable due to block scope.
  • Use const by default; use let only if the value will change.
  • Avoid polluting the global scope. Wrap your code in functions or modules.
  • Be mindful of closures and lexical scoping in nested functions.

Conclusion

Understanding scope is essential for writing robust JavaScript. Whether it’s global, function, or block scope, knowing how and where your variables live can help you avoid common bugs and write cleaner, more maintainable code.

By mastering scope, you’ll also understand other advanced concepts like closures, currying, and the module pattern with greater ease.

Happy coding! ✌️

--

--