JavaScript: JavaScript Scope
Last updated: 2026-10-13
Scope determines where a variable can be accessed. Think of it like an ID card — your city ID only works within your province, and your provincial ID won't help you abroad. Variables work the same way: once you leave their scope, they're unreachable.
1. Types of Scope and Their Rules
(1) Global Scope
Variables declared with var, let, or const at the outermost level have global scope and can be accessed from anywhere.
<script>
var globalVar = "I'm a global variable";
function test() {
console.log(globalVar); // Accessible
}
test();
</script>
Global variables are convenient but can easily pollute the namespace — as projects grow, naming conflicts become inevitable. Minimize global variable usage.
(2) Function Scope
Variables declared with var follow function scope: they only exist within the current function and disappear outside it.
<script>
function greet() {
var message = "Hello";
console.log(message); // Works fine
}
greet();
try {
console.log(message); // ReferenceError!
} catch(e) {
console.log("Access failed: " + e.message);
}
</script>
A classic gotcha with var's function scope: using var in a for loop means the variable "leaks" out after the loop ends.
(3) Block Scope
Variables declared with let and const follow block scope: they only exist within the current {}.
<script>
if (true) {
let x = 10;
const y = 20;
var z = 30;
console.log("Inside block: x=" + x + ", y=" + y + ", z=" + z);
}
console.log("Outside block: z=" + z); // var isn't restricted by blocks
try {
console.log(x); // x is not accessible here
} catch(e) {
console.log("x not accessible: " + e.message);
}
</script>
This is why loop counters should use let instead of var.
(4) Scope Chain
Inner scopes can access variables from outer scopes — like how you can use things from home at school (if you brought them). When looking up a variable, JavaScript searches from the current scope outward, layer by layer, until it reaches the global scope. This chain is called the scope chain.
<script>
const city = "Beijing";
function outer() {
const district = "Haidian";
function inner() {
const street = "Zhongguancun";
console.log(street, district, city); // All accessible
}
inner();
}
outer();
</script>
Outer scopes cannot access inner scope variables — just like you can't reach into a student's pocket from outside the classroom.
(5) Hoisting
var declarations are "hoisted" to the top of their scope, but assignments are not:
<script>
console.log(a); // undefined (not an error!)
var a = 5;
// Actual execution order: var a; → console.log(a); → a = 5;
</script>
let and const are also hoisted, but accessing them before declaration throws an error — this region is called the Temporal Dead Zone (TDZ):
<script>
try {
console.log(b); // ReferenceError!
} catch(e) {
console.log("Temporal dead zone: " + e.message);
}
let b = 5;
</script>
Bottom line: always use let and const; avoid the hoisting pitfalls of var.
(6) Closures Explained Intuitively
A closure = a function + the variable environment where it was born. It's like someone who left their hometown but still remembers what it looked like — even if the hometown changed later, their memory of it stays the same.
<script>
function createCounter() {
let count = 0; // This variable is "locked" inside the closure
return function() {
count++;
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
// The count variable wasn't destroyed because the returned function still "remembers" it
</script>
createCounter has already finished executing — logically, count should be destroyed. But the returned function holds tightly onto count, so it survives. That's a closure.
(7) Practical Closure Use Cases
- Counters: Record state without global variables (as shown above)
- Private variables: Variables inside closures can't be accessed directly from outside — only through the returned functions
- Callback functions: Maintain references to variables in event listeners and timers
▶ Example: var vs let in Loops
▶ Try it Yourself▶ Example: Scope Chain Demo
▶ Try it Yourself▶ Example: Closure Counter with Private Variables
▶ Try it Yourself▶ Example: Hoisting Comparison
▶ Try it Yourself❓ FAQ
var in a for loop cause all callbacks to output the same value?var has function scope — after the loop ends, there's only one i, and all callbacks share it (which by then equals the loop's final value). With let, each iteration gets its own independent copy of i.null.let or const?const; only use let when you need to reassign. This habit prevents you from accidentally modifying variables that shouldn't change.📖 Summary
- Global scope is visible everywhere; function scope is limited to a function; block scope is limited to
{} - The scope chain means inner scopes can access outer variables, but not vice versa
varhas function scope and hoisting;let/consthave block scope and temporal dead zone- Closures let functions "remember" their creation scope, useful for data privacy and factories
- Minimize global variables by encapsulating code in functions or modules
📝 Exercises
- Write a function
makeGreeter(greeting)that returns a function which, when called with a name, returnsgreeting + ", " + name. Create two greeting functions with different greetings and verify they don't interfere with each other. - Write a
createWallet(initialBalance)function that returns three methods:deposit,withdraw, andgetBalance. Thebalanceshould not be directly accessible from outside (private variable) — it can only be manipulated through these methods. - Explain the output of the following code and why:
<script>
for (var i = 0; i < 3; i++) {
setTimeout(function() { console.log(i); }, 100);
}
</script>
Then modify it to output 0, 1, 2.