Closures in JavaScript
Closure is a function along with its lexical environment forms a closure that is called closure.
What is lexical Environment?
In Simple language lexical scope is a variable defined outside your scope or upper scope is automatically available inside your scope which means you don't need to pass it here.
What happen line 4. so inside c() it forms a closure with the a() lexical scope. c() function is bind to the variable of b that means if forms a closure and it has access to parent lexical scope.let str = "javaScript";myFun = () => {console.log(str);}myFun();
consider this example x() it return function y so what will print in console, yeah it print function y likefunction x() {var a = 10;function y() {console.log(a);}return y;}var z = x();console.log(z);z();
function y(){
console.log(a);
}and what will be return after calling z()?----------------------------------------------------------------------------------------------------------------------------------------
Closures in JavaScript - The Complete Guide
Hey everyone! Welcome back to Namaste JavaScript. Today we are going to study one of the most beautiful and powerful concepts in JavaScript - Closures!
I am super excited about this topic because closures are everywhere in JavaScript. Once you understand closures, you will see them everywhere - in event handlers, callbacks, setTimeout, and more!
What we will cover:
- What is a Closure?
- Lexical Scope and Lexical Environment
- Function returning a Function
- Closure with Examples
- Closures and Scope Chain
- Uses of Closures
- Famous Interview Questions
Let's Start with a Basic Example
function x() {
var a = 7;
function y() {
console.log(a);
}
y();
}
x();
OUTPUT: 7
Simple, right? Function y() can access variable 'a' even though 'a' is not inside y(). This is because of lexical scope.
But wait - this is not closure yet. Let me show you what closure really is!
What is a Closure?
Let's look at the MDN definition:
"A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment)."
In simple words:
Closure = Function + Its Lexical Environment
When a function is returned from another function, it still remembers its lexical scope. It carries its lexical environment along with it!
The Classic Closure Example
function x() {
var a = 7;
function y() {
console.log(a);
}
return y; // Returning the function!
}
var z = x();
console.log(z);
OUTPUT:
function y() {
console.log(a);
}
We stored the returned function in variable z. Now here's the interesting part...
function x() {
var a = 7;
function y() {
console.log(a);
}
return y;
}
var z = x();
// x() is done executing!
// Its execution context is gone from call stack!
// Variable 'a' should be garbage collected, right?
z(); // What will happen?
OUTPUT: 7
MIND BLOWN! Even though x() has finished executing and its execution context is gone, z() still prints 7!
How is this possible?
When function y was returned, it was not returned alone. It was returned along with its closure - the lexical environment where it was created!
Closures Remember References, Not Values!
Very important point - closures don't remember the VALUE, they remember the REFERENCE to the variable!
function x() {
var a = 7;
function y() {
console.log(a);
}
a = 100; // Changed the value!
return y;
}
var z = x();
z();
OUTPUT: 100
Not 7! The closure remembers the reference to 'a', not the value 7. When 'a' changed to 100, the closure sees 100!
Nested Closures - Multiple Levels
function x() {
var a = 7;
function y() {
var b = 100;
function z() {
console.log(a, b);
}
z();
}
y();
}
x();
OUTPUT: 7 100
Function z() forms a closure with both y()'s scope and x()'s scope. It can access both 'a' and 'b'!
Let's See Closure in Browser DevTools
function x() {
var a = 7;
function y() {
debugger; // Put a breakpoint here!
console.log(a);
}
return y;
}
var z = x();
z();
If you open DevTools and check the Scope section, you will see:
Scope:
Local: (empty)
Closure (x): { a: 7 } // Here's the closure!
Global: { ... }
Browser explicitly shows "Closure" with the variables it has enclosed!
Closure with Nested Functions
function outest() {
var c = 20;
function outer(b) {
function inner() {
console.log(a, b, c);
}
var a = 10;
return inner;
}
return outer;
}
var close = outest()("Hello");
close();
OUTPUT: 10 "Hello" 20
Inner function has closure over:
- outer's scope: a = 10, b = "Hello"
- outest's scope: c = 20
Common Interview Pattern - Returning Anonymous Function
Instead of defining and returning separately, we can do it in one line:
function x() {
var a = 7;
return function y() {
console.log(a);
};
}
var z = x();
z();
OUTPUT: 7
Same thing! Function y still forms closure with variable 'a'.
Uses of Closures
Closures are used everywhere in JavaScript:
- Module Design Pattern
- Currying
- Functions like once()
- Memoization
- Maintaining state in async world
- setTimeout and setInterval
- Iterators
- Data hiding and encapsulation
Use Case 1: Data Hiding / Encapsulation
function counter() {
var count = 0; // Private variable!
return {
increment: function() {
count++;
console.log(count);
},
decrement: function() {
count--;
console.log(count);
}
};
}
var counter1 = counter();
counter1.increment();
counter1.increment();
counter1.decrement();
// count is NOT accessible from outside!
console.log(count); // Error!
OUTPUT: 1 2 1 ReferenceError: count is not defined
Variable 'count' is private! It can only be accessed through the returned functions. This is data hiding!
Use Case 2: Function that Runs Only Once
function once(fn) {
let called = false;
return function() {
if (!called) {
called = true;
return fn();
}
};
}
const hello = once(() => console.log("Hello!"));
hello(); // Hello!
hello(); // Nothing
hello(); // Nothing
OUTPUT: Hello! // (nothing printed for subsequent calls)
The inner function remembers 'called' variable through closure. Once it becomes true, the function never runs again!
Use Case 3: Memoization
function memoize(fn) {
const cache = {};
return function(n) {
if (n in cache) {
console.log("From cache!");
return cache[n];
}
console.log("Calculating...");
cache[n] = fn(n);
return cache[n];
};
}
const factorial = memoize(function(n) {
if (n <= 1) return 1;
return n * factorial(n - 1);
});
console.log(factorial(5));
console.log(factorial(5));
OUTPUT: Calculating... 120 From cache! 120
The cache object is remembered through closure!
Famous Interview Question: setTimeout in Loop
This is the MOST asked closure question in interviews!
function x() {
for (var i = 1; i <= 5; i++) {
setTimeout(function() {
console.log(i);
}, i * 1000);
}
}
x();
OUTPUT: (after 1, 2, 3, 4, 5 seconds respectively) 6 6 6 6 6
Why 6 and not 1, 2, 3, 4, 5?
Because of closure! All 5 setTimeout callbacks point to the SAME reference of 'i'. By the time they execute, the loop has finished and i = 6!
Solution 1: Use let instead of var
function x() {
for (let i = 1; i <= 5; i++) {
setTimeout(function() {
console.log(i);
}, i * 1000);
}
}
x();
OUTPUT: (after 1, 2, 3, 4, 5 seconds respectively) 1 2 3 4 5
'let' has block scope. Each iteration creates a new 'i' in memory!
Solution 2: Use closure with IIFE
function x() {
for (var i = 1; i <= 5; i++) {
(function(j) {
setTimeout(function() {
console.log(j);
}, j * 1000);
})(i);
}
}
x();
OUTPUT: (after 1, 2, 3, 4, 5 seconds respectively) 1 2 3 4 5
We create a new closure for each iteration by wrapping setTimeout in an IIFE. Each callback has its own copy of 'j'!
Disadvantages of Closures
- Memory consumption: Closed-over variables are not garbage collected
- Memory leaks: If not handled properly, can cause memory leaks
- Performance: Creating closures in loops can be expensive
Quick Recap - Interview Cheat Sheet
| Concept | Explanation |
|---|---|
| Closure | Function + Lexical Environment bundled together |
| Lexical Scope | Inner function can access outer function's variables |
| Reference | Closures remember references, not values |
| Scope Chain | Nested closures form a chain of scopes |
Interview Tips - How to Explain
Q: What is a Closure?
"A closure is a function bundled together with its lexical environment. When a function is returned from another function, it carries its lexical scope with it - it remembers the variables from its outer scope even after the outer function has finished executing."
Q: Why do closures exist?
"Closures exist because of lexical scoping in JavaScript. Functions remember where they were created and can access variables from that scope. This is fundamental to how JavaScript works."
Q: What are uses of closures?
"Closures are used for data hiding/encapsulation, module design pattern, currying, memoization, maintaining state in async operations, and functions like setTimeout callbacks."
Key Points to Remember
- Closure = Function + Lexical Environment
- Functions remember their birthplace (where they were created)
- Closures remember references, not values
- Nested functions create scope chain
- Closures enable data hiding and encapsulation
- let vs var in loops - different closure behavior
- Closures can cause memory leaks if not careful
- Most design patterns in JS use closures
Keep coding, keep learning!
Post a Comment