Understanding Scope in JavaScript: A Complete Guide
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, andconstinteract 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:
- Global Scope
- Function (Local) Scope
- 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
windowobject.
🔁 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 definedFunction 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 definedOn 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
importorexportis 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 allowedTDZ = Temporal Dead Zone
Note: While all three are hoisted,
letandconstremain in the temporal dead zone until they're declared, so accessing them before the declaration throws aReferenceError.
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 3Why? 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 2This 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
letandconstovervar—they are more predictable due to block scope. - Use
constby default; useletonly 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! ✌️








