Table of Contents
What is a Closure?
A closure is a function that remembers the variables from its outer scope, even after the outer function has finished executing. Closures are one of the most powerful features in JavaScript.
Here is a simple example:
function createGreeter(name) {
return function () {
console.log(`Hello, ${name}!`);
};
}
const greet = createGreeter('Shahnewaz');
greet(); // Hello, Shahnewaz!The inner function closes over the name variable. Even though createGreeter has returned, the inner function still has access to name.
Lexical Scope
JavaScript uses lexical scoping — the scope of a variable is determined by where it is written in the code, not where it is called.
const outer = 'I am outside';
function demo() {
console.log(outer); // Can access outer
}
demo();Variables defined in an outer scope are available to all inner scopes. This chain of scopes is called the scope chain.
Key idea: Closures capture variables by reference, not by value. If the variable changes after the closure is created, the closure sees the new value.
Practical Examples
Data Privacy
Closures let you create private variables:
function createCounter() {
let count = 0;
return {
increment() {
count += 1;
return count;
},
decrement() {
count -= 1;
return count;
},
getCount() {
return count;
}
};
}
const counter = createCounter();
counter.increment(); // 1
counter.increment(); // 2
counter.getCount(); // 2The count variable is not accessible from outside. Only the returned methods can read or modify it.
Function Factories
You can use closures to create specialized functions:
function multiply(factor) {
return function (number) {
return number * factor;
};
}
const double = multiply(2);
const triple = multiply(3);
double(5); // 10
triple(5); // 15Event Handlers
Closures are commonly used in event handlers:
function setupButton(buttonId, message) {
const button = document.getElementById(buttonId);
button.addEventListener('click', function () {
alert(message);
});
}
setupButton('btn1', 'First button clicked');
setupButton('btn2', 'Second button clicked');Each handler remembers its own message value.
Common Patterns
Closures show up in a lot of everyday JavaScript code. Here are some patterns you have probably already used without realizing it:
Data privacy — Keeps internal state hidden from the outside. You have seen this in createCounter().
Function factory — Builds new functions on the fly. Think of multiply(factor).
Memoization — Remembers past results so you do not redo the work. Common in cachedFetch(url).
Debouncing — Waits until things settle down before running. Used in debounce(fn, delay).
Module pattern — Groups related logic into a single unit. Like createModule().
Memoization Example
function memoize(fn) {
const cache = {};
return function (...args) {
const key = JSON.stringify(args);
if (cache[key] !== undefined) {
return cache[key];
}
const result = fn(...args);
cache[key] = result;
return result;
};
}
const expensiveSquare = memoize(function (n) {
console.log('Computing...');
return n * n;
});
expensiveSquare(5); // Computing... 25
expensiveSquare(5); // 25 (cached, no "Computing...")Pitfalls to Avoid
Loop Variable Capture
The classic closure-in-a-loop problem:
// Bad: all functions share the same `i`
for (var i = 0; i < 3; i++) {
setTimeout(function () {
console.log(i);
}, 100);
}
// Output: 3, 3, 3Fix 1: Use let instead of var:
for (let i = 0; i < 3; i++) {
setTimeout(function () {
console.log(i);
}, 100);
}
// Output: 0, 1, 2Fix 2: Use an IIFE to create a new scope:
for (var i = 0; i < 3; i++) {
(function (j) {
setTimeout(function () {
console.log(j);
}, 100);
})(i);
}
// Output: 0, 1, 2Memory Leaks
Closures keep references to outer variables. If a closure is long-lived, those variables are never garbage collected.
Warning: Be careful with closures that reference large objects or DOM elements. Clean up references when they are no longer needed.
Conclusion
Closures are fundamental to JavaScript. They enable data privacy, function factories, memoization, and many other patterns. Understanding closures will make you a stronger developer.
Tip: If you can explain why
createCounter()works, you understand closures.
Further reading: