Introduction
A closure is when a function keeps access to variables from its outer scope, even after that outer function has finished running.
What is a Closure?
When a function is created inside another function, it automatically remembers the variables around it. Even when the outer function is done running, the inner function still has access to those variables.
JavaScript does this by design. The inner function carries a reference to the variables it used when it was created. This is called a closure.
Basic Example
function outer() {
let count = 0;
function inner() {
count++;
console.log(count);
}
return inner;
}
const fn = outer();
fn(); // 1
fn(); // 2
fn(); // 3
What’s happening here?
- outer() creates a variable count and defines a function inner()
- It returns the inner() function
- It holds that inner() function
- Every time you call fn(), it increases count and logs it
Even though outer() has already finished, inner() still remembers the variable count. That’s the closure.
Why are Closures Useful?
Closures help you.
- Keep state (like a counter or value)
- Hide internal variables so no one else can touch them
- Build reusable, customized functions
Let’s look at some practical examples.
Use Case 1. Private Variables
function createCounter() {
let count = 0;
return {
increment: function () {
count++;
return count;
},
decrement: function () {
count--;
return count;
}
};
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.decrement()); // 1
Here
- Count is not available outside
- Only increment and decrement can access it
- This keeps the state private
Use Case 2. Custom Functions with Pre-set Values
function multiply(x) {
return function (y) {
return x * y;
};
}
const double = multiply(2);
console.log(double(5)); // 10
console.log(double(10)); // 20
The double function remembers that x is 2. That’s a closure in action.
Use Case 3. Event Handlers That Track State
function setupClickTracker(id) {
let clicks = 0;
document.getElementById(id).addEventListener('click', function () {
clicks++;
console.log("Clicked " + clicks + " times");
});
}
Each button you set up like this will keep its click count. That’s possible because of closures.
Common Mistake: Loop with var
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
- Expected: 0 1 2
- Actual: 3 3 3
Why? Because var is function-scoped. All the functions share the same i. By the time the timeout runs, i has already become 3.
Fix with let
for (let i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
This prints 0 1 2 as expected. Because let is block-scoped, a new i is created for each loop iteration. Each closure gets the correct value.
How Closures Work Internally?
When you return a function from another function, JavaScript doesn't throw away the variables the inner function used. It keeps them alive as long as that inner function exists.
So even if the outer function is done running, the variables are still available to the inner function. That’s what allows the inner function to keep working with the original values.
Memory Concerns
Closures can cause memory problems if you're not careful. For example.
function setup() {
const largeData = new Array(1000000).fill('*');
return function () {
console.log("Running...");
};
}
const fn = setup(); // largeData is now stuck in memory
Even though largeData is never used again, it's not removed because the returned function might use it. This can waste memory.
Always think about what variables your closures are holding on to.
Classic Interview Trap
var funcs = [];
for (var i = 0; i < 3; i++) {
funcs.push(function() {
console.log(i);
});
}
funcs[0](); // 3
funcs[1](); // 3
funcs[2](); // 3
All print 3. Why? Same issue as before var is shared.
Fix using IIFE
for (var i = 0; i < 3; i++) {
(function(index) {
funcs.push(function() {
console.log(index);
});
})(i);
}
This time, each function remembers its value of index.
Quick Summary
- A closure is when a function remembers variables from its outer function, even after the outer function is done.
- Closures help you keep state, create private variables, and write more flexible code.
- Be careful when using closures in loops or with large data, or you might end up with bugs or memory issues.
- Always know what variables your function is capturing.