Scope
Learn how JavaScript decides where variables are accessible using global, function, and block scope.
Scope
When you create a variable, it is not automatically available everywhere in your code. Scope determines where a variable can be accessed and where it cannot.
Think of scope like rooms in a house. A variable created in the bedroom is only accessible in the bedroom. But a variable in the hallway — the shared space — is accessible from every room.
let hallway = "I am accessible everywhere";
function bedroom() {
let private = "Only accessible in here";
console.log(hallway); // ✅ works — hallway is in outer scope
console.log(private); // ✅ works — we are inside bedroom
}
bedroom();
console.log(private); // ❌ ReferenceError — private is not accessible out hereJavaScript has three types of scope — global, function, and block.
The Three Scopes
1. Global Scope
A variable declared outside of any function or block lives in the global scope. It is accessible from anywhere in your code.
const siteName = "DocsHub";
let visitorCount = 0;
function showSite() {
console.log(siteName); // ✅ accessible
visitorCount++; // ✅ accessible
}
function logVisit() {
console.log(`Visitors: ${visitorCount}`); // ✅ accessible
}
showSite();
logVisit(); // Visitors: 1Both functions can read and update visitorCount because it lives in the global scope.
Avoid creating too many global variables. Every piece of your code can accidentally overwrite them — and in large projects this causes hard-to-find bugs. Keep variables as local as possible.
2. Function Scope
Variables declared inside a function are only accessible inside that function. They are created when the function runs and destroyed when it finishes.
function calculateTax() {
const taxRate = 0.17; // only exists inside calculateTax
let amount = 1000;
console.log(amount * taxRate); // 170
}
calculateTax();
console.log(taxRate); // ❌ ReferenceError — taxRate does not exist out here
console.log(amount); // ❌ ReferenceError — amount does not exist out hereEach function call gets its own fresh scope. Variables from one call do not leak into another.
function greet(name) {
let message = `Hello, ${name}!`; // fresh every call
console.log(message);
}
greet("Ali"); // Hello, Ali!
greet("Sara"); // Hello, Sara!message is created fresh on every call and thrown away when the function ends.
3. Block Scope
A block is any code inside curly braces {} — an if statement, a loop, or just a standalone block. Variables declared with let and const inside a block are only accessible within that block.
if (true) {
let blockVar = "I am block scoped";
const alsoBlock = "Me too";
console.log(blockVar); // ✅ works
}
console.log(blockVar); // ❌ ReferenceError
console.log(alsoBlock); // ❌ ReferenceErrorThis is exactly why var was problematic — it ignores block scope:
if (true) {
var leaked = "I escape the block";
let contained = "I stay here";
}
console.log(leaked); // ✅ "I escape the block" — var leaks out
console.log(contained); // ❌ ReferenceError — let stays containedThis is one of the main reasons var was replaced by let and const.
Nested Scope — Scope Chain
Scopes can be nested inside each other. When JavaScript looks for a variable, it starts in the current scope and works its way outward until it finds it — or throws an error.
This outward lookup is called the scope chain.
const global = "global";
function outer() {
const outerVar = "outer";
function inner() {
const innerVar = "inner";
// inner can see all three — its own, outer's, and global
console.log(innerVar); // ✅ "inner"
console.log(outerVar); // ✅ "outer" — found in outer scope
console.log(global); // ✅ "global" — found in global scope
}
// outer can see its own and global — but not inner's
console.log(outerVar); // ✅ "outer"
console.log(global); // ✅ "global"
console.log(innerVar); // ❌ ReferenceError — inner is not accessible up here
inner();
}
outer();The lookup only goes outward — never inward. Inner functions can see outer variables, but outer functions cannot see inner variables.
Variable Shadowing
When a variable in an inner scope has the same name as one in an outer scope, the inner one shadows the outer one — it takes priority inside that scope.
const language = "Python";
function printLanguage() {
const language = "JavaScript"; // shadows the outer language
console.log(language); // "JavaScript" — inner wins
}
printLanguage();
console.log(language); // "Python" — outer is unchangedThe outer language is not changed — it is simply hidden inside printLanguage. Once you leave the function, the outer one is visible again.
Shadowing is not a bug — but accidental shadowing can cause confusion. Be intentional with variable names.
Lexical Scope
JavaScript uses lexical scope — meaning scope is determined by where you write the code, not where you run it.
const name = "Ali";
function printName() {
console.log(name); // looks up in the scope where printName was WRITTEN
}
function callPrint() {
const name = "Sara"; // this does not affect printName
printName();
}
callPrint(); // "Ali" — not "Sara"printName was written in the global scope — so it looks up name in the global scope, even when called from inside callPrint. Where the function is called from does not matter — only where it was defined.
This is the foundation of one of the most important JavaScript concepts — closures — which are covered in the Advanced section.
A Real Example — Score Tracker
let highScore = 0; // global — shared across the game
function playRound(playerName) {
let score = 0; // local to this round
// simulate scoring
for (let i = 0; i < 3; i++) { // i is block scoped to this loop
let points = Math.floor(Math.random() * 10) + 1;
score += points;
}
console.log(`${playerName} scored: ${score}`);
if (score > highScore) {
highScore = score; // update the global high score
console.log(`New high score: ${highScore}!`);
}
}
playRound("Ali");
playRound("Sara");
playRound("Zara");
console.log(`Final high score: ${highScore}`);highScore— global, shared and updated across all roundsscore— function scoped, fresh for every roundpointsandi— block scoped, exist only inside the loop
Each variable lives at exactly the right level of scope for its purpose.
Summary
- Scope determines where a variable is accessible in your code
- Global scope — accessible everywhere, declared outside all functions and blocks
- Function scope — accessible only inside the function where it was declared
- Block scope — accessible only inside the
{}block — onlyletandconstrespect block scope,vardoes not - Scope chain — when a variable is not found in the current scope, JavaScript looks outward until it finds it or throws a
ReferenceError - Variable shadowing — an inner variable with the same name as an outer one takes priority inside its scope
- Lexical scope — scope is determined by where the code is written, not where it is called from
- Keep variables as local as possible — avoid polluting the global scope