A look at scope and closure in JavaScript: where variables are visible, and why an inner function can still read variables from the function that created it.
What is scope?
Scope is about where a variable can be accessed. There are two main types: global and local.
Global scope
A variable declared outside any function has global scope. It can be accessed anywhere in your code.
Example
let globalVar = "I am global";
function testScope() {
console.log(globalVar); // Accessible here
}
console.log(globalVar); // And here
Local scope
Variables declared inside a function are locally scoped. They only exist within that function.
Example
function testLocalScope() {
let localVar = "I am local";
console.log(localVar); // Accessible here
}
testLocalScope();
// console.log(localVar); // Error: localVar is not defined outside the function
Block scope in ES6
ES6 introduced let and const, which are scoped to the block they are declared in – an if statement, a loop, and so on.
Example
if (true) {
let blockVar = "I am block-scoped";
console.log(blockVar); // Accessible here
}
// console.log(blockVar); // Error: blockVar is not defined outside the block
Understanding closure
A closure happens when a function remembers and can access variables from its outer scope, even after that outer function has finished running.
Example
function makeGreeting() {
let name = "Alice";
return function() {
console.log("Hello " + name);
};
}
let greetAlice = makeGreeting(); // The function makeGreeting has returned
greetAlice(); // Outputs: Hello Alice
greetAlice is a closure. It still has access to name from makeGreeting, even though makeGreeting has already returned.
Why are closures useful?
Closures are useful for several reasons:
- Data encapsulation: create private variables that only inner functions can access.
- Maintaining state: keep values available in callbacks and asynchronous code.
- Currying and function factories: build functions that return other functions with preset values.
Example – function factory
function makeMultiplier(multiplier) {
return function (number) {
return number * multiplier;
};
}
let double = makeMultiplier(2);
console.log(double(5)); // Outputs: 10
Common pitfalls with closure
Closures inside loops are a common source of bugs.
Example of a pitfall
for (var i = 1; i <= 3; i++) {
setTimeout(function() {
console.log("i: " + i); // Outputs "i: 4" three times, not the expected 1, 2, 3
}, i * 1000);
}
The variable i is shared across every iteration. By the time the setTimeout callbacks run, the loop has finished and i is 4.
Solution using IIFE (Immediately Invoked Function Expression)
for (var i = 1; i <= 3; i++) {
(function(j) {
setTimeout(function() {
console.log("j: " + j); // Correctly outputs 1, 2, 3
}, j * 1000);
})(i);
}
Scope and closure sit at the heart of how JavaScript manages variables. Once they click, a lot of patterns in real code start to make sense.

