Skip to content

Commit ab88dd7

Browse files
Add JavaScript closures guide and examples
Introduces a comprehensive README explaining JavaScript closures with detailed examples and explanations. Adds closures.js containing practical code samples covering closure basics, data encapsulation, common pitfalls, function factories, async behavior, currying, and memory leak considerations.
0 parents  commit ab88dd7

File tree

2 files changed

+349
-0
lines changed

2 files changed

+349
-0
lines changed

Closures/README.md

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
# JavaScript Closures: In-Depth Guide with Examples
2+
The concept of **closures** in JavaScript, using the code examples from `closures.js`.
3+
4+
---
5+
6+
## 1. Basic Closure Example
7+
```js
8+
function outerFunction() {
9+
let outerVariable = 'I am from outer!';
10+
function innerFunction() {
11+
console.log(outerVariable);
12+
}
13+
return innerFunction;
14+
}
15+
const closure1 = outerFunction();
16+
closure1(); // Output: I am from outer!
17+
```
18+
**Explanation:**
19+
- `innerFunction` forms a closure over `outerVariable` from `outerFunction`.
20+
- Even after `outerFunction` finishes, `innerFunction` can access `outerVariable`.
21+
22+
---
23+
24+
## 2. Closure with Private Variables (Data Encapsulation)
25+
```js
26+
function makeCounter() {
27+
let count = 0;
28+
return function() {
29+
count++;
30+
return count;
31+
};
32+
}
33+
const counter = makeCounter();
34+
counter(); // 1
35+
counter(); // 2
36+
counter(); // 3
37+
```
38+
**Explanation:**
39+
- `count` is private to the returned function, not accessible from outside.
40+
- Each call to `counter()` increments and returns the private `count`.
41+
42+
---
43+
44+
## 3. Closures in Loops (Common Pitfall & Solution)
45+
**Problem:**
46+
```js
47+
var funcs = [];
48+
for (var i = 0; i < 3; i++) {
49+
funcs.push(function() {
50+
console.log('Problem:', i);
51+
});
52+
}
53+
funcs[0](); // 3
54+
funcs[1](); // 3
55+
funcs[2](); // 3
56+
```
57+
**Solution 1: IIFE**
58+
```js
59+
var funcsFixed = [];
60+
for (var j = 0; j < 3; j++) {
61+
(function(jCopy) {
62+
funcsFixed.push(function() {
63+
console.log('Fixed:', jCopy);
64+
});
65+
})(j);
66+
}
67+
funcsFixed[0](); // 0
68+
funcsFixed[1](); // 1
69+
funcsFixed[2](); // 2
70+
```
71+
**Solution 2: Use `let`**
72+
```js
73+
let funcsLet = [];
74+
for (let k = 0; k < 3; k++) {
75+
funcsLet.push(function() {
76+
console.log('Let:', k);
77+
});
78+
}
79+
funcsLet[0](); // 0
80+
funcsLet[1](); // 1
81+
funcsLet[2](); // 2
82+
```
83+
**Explanation:**
84+
- Using `var` causes all functions to share the same variable.
85+
- IIFE or `let` creates a new scope for each iteration.
86+
87+
---
88+
89+
## 4. Closures for Function Factories
90+
```js
91+
function makeMultiplier(multiplier) {
92+
return function(x) {
93+
return x * multiplier;
94+
};
95+
}
96+
const double = makeMultiplier(2);
97+
const triple = makeMultiplier(3);
98+
double(5); // 10
99+
triple(5); // 15
100+
```
101+
**Explanation:**
102+
- `makeMultiplier` returns a function that remembers the `multiplier` value.
103+
104+
---
105+
106+
## 5. Closures in Asynchronous Code
107+
**Problem:**
108+
```js
109+
for (var m = 0; m < 3; m++) {
110+
setTimeout(function() {
111+
console.log('Async Problem:', m);
112+
}, 100);
113+
}
114+
// All log 3
115+
```
116+
**Solution:**
117+
```js
118+
for (let n = 0; n < 3; n++) {
119+
setTimeout(function() {
120+
console.log('Async Fixed:', n);
121+
}, 200);
122+
}
123+
// Logs 0, 1, 2
124+
```
125+
**Explanation:**
126+
- `let` creates a new binding for each iteration, so closures capture the correct value.
127+
128+
---
129+
130+
## 6. Practical Example: Hiding Implementation Details
131+
```js
132+
function Person(name) {
133+
let _name = name;
134+
return {
135+
getName: function() { return _name; },
136+
setName: function(newName) { _name = newName; }
137+
};
138+
}
139+
const p = Person('Alice');
140+
p.getName(); // Alice
141+
p.setName('Bob');
142+
p.getName(); // Bob
143+
```
144+
**Explanation:**
145+
- `_name` is private, only accessible via `getName` and `setName`.
146+
- Demonstrates data privacy using closures.
147+
148+
---
149+
150+
## 7. Interview Question: What is a closure?
151+
> A closure is a function that remembers its lexical scope even when the function is executed outside that scope. It allows functions to access variables from an enclosing scope, even after that scope has closed.
152+
153+
## 8. Interview Question: Why are closures useful?
154+
- Data privacy (private variables)
155+
- Function factories
156+
- Partial application/currying
157+
- Event handlers and callbacks
158+
- Maintaining state in async code
159+
160+
---
161+
162+
## 9. Advanced: Currying with Closures
163+
```js
164+
function add(a) {
165+
return function(b) {
166+
return a + b;
167+
};
168+
}
169+
const add5 = add(5);
170+
add5(10); // 15
171+
```
172+
**Explanation:**
173+
- `add` returns a function that remembers `a`.
174+
- This is the basis for currying and partial application.
175+
176+
---
177+
178+
179+
## 10. Memory Leaks and Closures
180+
> Be careful: Closures can cause memory leaks if they retain references to large objects unnecessarily. Always clean up references if not needed.
181+
182+
**Example:**
183+
```js
184+
function createLeak() {
185+
let largeArray = new Array(1e6).fill('leak'); // Large object
186+
return function() {
187+
// This closure keeps largeArray in memory
188+
console.log('Still holding largeArray of length:', largeArray.length);
189+
};
190+
}
191+
192+
let leaky = createLeak();
193+
// Even if we don't need largeArray anymore, it's not garbage collected
194+
// because the closure (leaky) still references it.
195+
// To avoid the leak, set leaky = null when done:
196+
// leaky = null;
197+
```
198+
199+
---
200+
201+
## Summary
202+
Closures are a powerful feature in JavaScript, enabling data privacy, function factories, and more. Understanding closures is essential for interviews and real-world development.
203+
204+
---
205+
206+
**Practice:** Try modifying the examples in `closures.js` and observe the results to deepen your understanding.

Closures/closures.js

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
2+
// 1. Basic Closure Example
3+
function outerFunction() {
4+
let outerVariable = 'Mai outer se hu!';
5+
function innerFunction() {
6+
console.log(outerVariable); // innerFunction has access to outerVariable
7+
}
8+
return innerFunction;
9+
}
10+
11+
const closure1 = outerFunction();
12+
closure1(); // Output: Mai outer se hu!
13+
14+
// 2. Closure with Private Variables (Data Encapsulation)
15+
function makeCounter() {
16+
let count = 0;
17+
return function() {
18+
count++;
19+
return count;
20+
};
21+
}
22+
23+
const counter = makeCounter();
24+
const counter2 = makeCounter(); // Separate counter instance
25+
console.log(counter()); // 1
26+
console.log(counter()); // 2
27+
console.log(counter2()); // 1
28+
console.log(counter()); // 3
29+
30+
// 3. Closures in Loops (Common Pitfall & Solution)
31+
// Problem: All functions log the same value
32+
var funcs = [];
33+
for (var i = 0; i < 3; i++) {
34+
funcs.push(function() {
35+
console.log('Problem:', i);
36+
});
37+
}
38+
funcs[0](); // Problem: 3
39+
funcs[1](); // Problem: 3
40+
funcs[2](); // Problem: 3
41+
42+
// Solution: Use IIFE or let
43+
var funcsFixed = [];
44+
for (var j = 0; j < 3; j++) {
45+
(function(jCopy) {
46+
funcsFixed.push(function() {
47+
console.log('Fixed:', jCopy);
48+
});
49+
})(j);
50+
}
51+
funcsFixed[0](); // Fixed: 0
52+
funcsFixed[1](); // Fixed: 1
53+
funcsFixed[2](); // Fixed: 2
54+
55+
// Or use let (ES6)
56+
let funcsLet = [];
57+
for (let k = 0; k < 3; k++) {
58+
funcsLet.push(function() {
59+
console.log('Let:', k);
60+
});
61+
}
62+
funcsLet[0](); // Let: 0
63+
funcsLet[1](); // Let: 1
64+
funcsLet[2](); // Let: 2
65+
66+
// 4. Closures for Function Factories
67+
function makeMultiplier(multiplier) {
68+
return function(x) {
69+
return x * multiplier;
70+
};
71+
}
72+
73+
const double = makeMultiplier(2);
74+
const triple = makeMultiplier(3);
75+
console.log(double(5)); // 10
76+
console.log(triple(5)); // 15
77+
78+
// 5. Closures in Asynchronous Code
79+
for (var m = 0; m < 3; m++) {
80+
setTimeout(function() {
81+
console.log('Async Problem:', m);
82+
}, 100);
83+
}
84+
// All log 3
85+
86+
for (let n = 0; n < 3; n++) {
87+
setTimeout(function() {
88+
console.log('Async Fixed:', n);
89+
}, 200);
90+
}
91+
// Logs 0, 1, 2
92+
93+
// 6. Practical Example: Hiding Implementation Details
94+
function Person(name) {
95+
let _name = name;
96+
return {
97+
getName: function() { return _name; },
98+
setName: function(newName) { _name = newName; }
99+
};
100+
}
101+
102+
const p = Person('Aditya');
103+
console.log(p.getName()); // Aditya
104+
p.setName('Bablu');
105+
console.log(p.getName()); // Bablu
106+
107+
// 7. Interview Questions: What is a closure?
108+
// A closure is a function that remembers its lexical scope even when the function is executed outside that scope.
109+
// It allows functions to access variables from an enclosing scope, even after that scope has closed.
110+
111+
// 8. Interview Questions: Why are closures useful?
112+
// - Data privacy (private variables)
113+
// - Function factories
114+
// - Partial application/currying
115+
// - Event handlers and callbacks
116+
// - Maintaining state in async code
117+
118+
// 9. Advanced: Currying with Closures
119+
function add(a) {
120+
return function(b) {
121+
return a + b;
122+
};
123+
}
124+
const add5 = add(5);
125+
console.log(add5(10)); // 15
126+
127+
// 10. Memory Leaks and Closures
128+
// Be careful: Closures can cause memory leaks if they retain references to large objects unnecessarily.
129+
130+
// Example: Memory Leak with Closures
131+
function createLeak() {
132+
let largeArray = new Array(1e6).fill('leak'); // Large object
133+
return function() {
134+
// This closure keeps largeArray in memory
135+
console.log('Still holding largeArray of length:', largeArray.length);
136+
};
137+
}
138+
139+
let leaky = createLeak();
140+
// Even if we don't need largeArray anymore, it's not garbage collected
141+
// because the closure (leaky) still references it.
142+
// To avoid the leak, set leaky = null when done:
143+
// leaky = null;

0 commit comments

Comments
 (0)