What are Closures in JavaScript?
In JavaScript, a closure is formed when an inner function has access to variables from an outer function’s scope, even after the outer function has returned. This is possible because functions in JavaScript form lexical environments at the time of their definition.
Why Closures Are Important?
- They allow data privacy (encapsulation).
- They help maintain state between function calls.
- They are fundamental in building factory functions and modules.
Example 1: Basic Closure
Let’s start with a simple example to see how closures retain access to outer function variables.
function outer() {
let name = "JavaScript";
function inner() {
console.log("Hello from " + name);
}
return inner;
}
const greet = outer();
greet(); // calling inner function
Output:
Hello from JavaScript
Explanation:
When outer()
is called, it returns the inner
function. Even though outer()
has finished executing, the returned inner
function still "remembers" the variable name
from its lexical scope. This is closure in action.
Question:
Why does the inner function still have access to name
after outer()
returns?
Answer: Because the inner function forms a closure and "closes over" the variable name
. JavaScript's function scope retains access to variables from the context in which the function was defined.
Example 2: Closures to Create Private Variables
Closures are often used to create data that cannot be directly accessed from the outside.
function createCounter() {
let count = 0;
return function() {
count++;
console.log("Count is", count);
}
}
const counter1 = createCounter();
counter1(); // Count is 1
counter1(); // Count is 2
Output:
Count is 1 Count is 2
Explanation:
The variable count
is not accessible from outside createCounter()
. But the returned function can access and modify it. This is a powerful technique for data privacy.
Question:
What happens if we create another counter?
const counter2 = createCounter();
counter2(); // Count is 1
Output:
Count is 1
Each call to createCounter()
returns a new closure with its own private count
.
Example 3: Closures Inside Loops (Common Interview Trap)
Let’s look at how closures behave inside loops and how to avoid common pitfalls.
// Common mistake
for (var i = 1; i <= 3; i++) {
setTimeout(function() {
console.log("i:", i);
}, i * 1000);
}
Output:
i: 4 i: 4 i: 4
Why 4?
The setTimeout
callback forms a closure over the i
variable, which after the loop ends is 4. All three closures reference the same i
.
Fix using IIFE:
for (var i = 1; i <= 3; i++) {
(function(j) {
setTimeout(function() {
console.log("i:", j);
}, j * 1000);
})(i);
}
Output:
i: 1 i: 2 i: 3
Fix using let:
for (let i = 1; i <= 3; i++) {
setTimeout(function() {
console.log("i:", i);
}, i * 1000);
}
Using let
ensures that each iteration of the loop has its own separate block scope for i
.
Key Points to Remember
- A closure gives you access to an outer function’s variables even after the outer function has returned.
- Closures can be used to implement data hiding and encapsulation.
- Closures remember the environment in which they were created — not where they are called.
Practice Questions
- What will be logged to the console in each closure example above?
- How can you use closures to implement private methods?
- Can closures be garbage collected? Under what conditions?