Skip to content

Commit 1c958b8

Browse files
added Memoization-in-JS
1 parent b979937 commit 1c958b8

File tree

2 files changed

+178
-1
lines changed

2 files changed

+178
-1
lines changed

Hard/Memoization-in-JS.md

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
### **What is Memoization in JavaScript ?**
2+
3+
Memoization is an optimization technique used in programming to improve the performance of functions by storing the results of expensive function calls and returning the cached result when the same inputs occur again. In JavaScript, memoization is particularly useful when dealing with recursive functions, computationally intensive operations, or functions that are called multiple times with the same arguments.
4+
5+
---
6+
7+
### **How Memoization Works**
8+
9+
Memoization works by maintaining a cache (a data structure like an object or a Map) where results of function calls are stored. The function checks this cache before performing any computation:
10+
11+
- **If a result exists for the given input(s):** The cached result is returned, saving time and resources.
12+
- **If a result does not exist:** The function computes the result, stores it in the cache, and then returns it.
13+
14+
---
15+
16+
### **Key Benefits of Memoization**
17+
18+
1. **Performance Improvement:** Reduces redundant calculations by reusing previously computed results.
19+
2. **Efficiency in Recursion:** Particularly useful for recursive algorithms like Fibonacci sequence or factorial computations.
20+
3. **Reduced Resource Usage:** Avoids repeated execution of heavy computations, saving CPU cycles.
21+
22+
---
23+
24+
### **Memoization in Practice**
25+
26+
#### **Basic Example of Memoization**
27+
28+
Here’s a simple implementation of memoization in JavaScript:
29+
30+
```javascript
31+
function memoize(fn) {
32+
const cache = new Map(); // Using a Map to store results for better performance
33+
return function (...args) {
34+
const key = JSON.stringify(args); // Serialize arguments as the cache key
35+
if (cache.has(key)) {
36+
console.log("Fetching from cache:", key);
37+
return cache.get(key); // Return cached result if available
38+
}
39+
console.log("Computing result for:", key);
40+
const result = fn(...args); // Compute the result
41+
cache.set(key, result); // Store the result in the cache
42+
return result;
43+
};
44+
}
45+
46+
// Example function to memoize
47+
function add(a, b) {
48+
return a + b;
49+
}
50+
51+
// Memoized version of the function
52+
const memoizedAdd = memoize(add);
53+
54+
console.log(memoizedAdd(1, 2)); // Computing result for: [1,2]
55+
console.log(memoizedAdd(1, 2)); // Fetching from cache: [1,2]
56+
console.log(memoizedAdd(2, 3)); // Computing result for: [2,3]
57+
```
58+
59+
#### **Explanation of the Example:**
60+
61+
1. The `memoize` function creates a wrapper around the provided function (`add` in this case).
62+
2. A `Map` is used to store the cached results.
63+
3. Each function call is checked against the cache. If the result exists, it is fetched directly; otherwise, it is computed and stored.
64+
65+
---
66+
67+
### **Use Case: Recursive Functions (Fibonacci)**
68+
69+
Recursive functions often involve repeated computations for the same inputs. Memoization can drastically improve their efficiency.
70+
71+
#### **Naive Fibonacci Function**
72+
73+
```javascript
74+
function fibonacci(n) {
75+
if (n <= 1) return n;
76+
return fibonacci(n - 1) + fibonacci(n - 2);
77+
}
78+
79+
console.log(fibonacci(10)); // Takes exponential time (O(2^n))
80+
```
81+
82+
#### **Optimized Fibonacci with Memoization**
83+
84+
```javascript
85+
function memoize(fn) {
86+
const cache = new Map();
87+
return function (...args) {
88+
const key = args[0]; // Using the first argument as the key
89+
if (cache.has(key)) {
90+
return cache.get(key);
91+
}
92+
const result = fn(...args);
93+
cache.set(key, result);
94+
return result;
95+
};
96+
}
97+
98+
const fibonacci = memoize(function (n) {
99+
if (n <= 1) return n;
100+
return fibonacci(n - 1) + fibonacci(n - 2);
101+
});
102+
103+
console.log(fibonacci(10)); // Much faster with O(n) complexity
104+
```
105+
106+
#### **Explanation:**
107+
108+
- Without memoization, the Fibonacci function repeatedly computes the same values.
109+
- With memoization, results for each `n` are stored and reused, reducing the time complexity from \(O(2^n)\) to \(O(n)\).
110+
111+
---
112+
113+
### **Memoization in Modern JavaScript**
114+
115+
#### **Using Closures**
116+
117+
Memoization can leverage closures to encapsulate the cache and function logic.
118+
119+
```javascript
120+
function memoizedFactorial() {
121+
const cache = {};
122+
return function factorial(n) {
123+
if (n in cache) {
124+
return cache[n];
125+
}
126+
if (n === 0 || n === 1) {
127+
return 1;
128+
}
129+
const result = n * factorial(n - 1);
130+
cache[n] = result;
131+
return result;
132+
};
133+
}
134+
135+
const factorial = memoizedFactorial();
136+
console.log(factorial(5)); // Computes and caches results
137+
console.log(factorial(5)); // Fetches from cache
138+
```
139+
140+
---
141+
142+
### **Advantages of Memoization**
143+
144+
- **Efficiency:** Saves computational resources by avoiding redundant calculations.
145+
- **Speed:** Accelerates execution for functions with repeated calls and identical inputs.
146+
- **Improved Recursive Functions:** Particularly valuable in recursive algorithms like Fibonacci, factorial, and dynamic programming problems.
147+
148+
---
149+
150+
### **Challenges and Best Practices**
151+
152+
1. **Cache Size Management:** Large caches may consume significant memory. Consider setting a limit.
153+
- Use strategies like Least Recently Used (LRU) caching.
154+
- Libraries like `lru-cache` implement such mechanisms efficiently.
155+
2. **Cache Keys:** Ensure the uniqueness of cache keys.
156+
157+
- Use robust serialization methods for complex arguments (e.g., `JSON.stringify`).
158+
159+
3. **Suitability:** Memoization is not always suitable for functions with side effects or unpredictable outputs (e.g., random numbers, API calls).
160+
161+
4. **Testing and Debugging:** Caching introduces complexity. Test thoroughly to avoid unexpected results.
162+
163+
---
164+
165+
### **When to Use Memoization**
166+
167+
- **Recursive Functions:** E.g., Fibonacci, factorial, dynamic programming.
168+
- **Expensive Computations:** Calculations that consume significant time and resources.
169+
- **Repeated Calls with Same Inputs:** Situations where functions are invoked with identical arguments multiple times.
170+
171+
---
172+
173+
### **Conclusion**
174+
175+
Memoization is a powerful technique in JavaScript to enhance the performance of functions by caching results for repeated inputs. While it is highly effective in specific scenarios like recursion and expensive computations, it should be used judiciously to avoid unnecessary complexity or memory overhead. With the right implementation, memoization can significantly improve the efficiency of your JavaScript code.
176+
177+
---

Hard/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ This section contains advanced JavaScript interview questions. Click on each que
66
2. [What is the purpose of the async and await keywords?](Purpose-of-async-and-await.md)
77
3. [Explain the working of the Event Loop.](Working-of-event-loop.md)
88
4. [How can you remove duplicates from an array in JavaScript?](Remove-duplicates-from-array.md)
9-
5. [What is memoization in JavaScript?](Memoization-in-JS.md)
9+
5. [What is memoization in JavaScript?](Memoization-in-JS.md)
1010
6. [What is the difference between innerHTML and innerText?](Difference-between-innerHTML-and-innerText.md)
1111
7. [Explain how localStorage, sessionStorage, and cookies differ.](Difference-between-localStorage-sessionStorage-cookies.md)
1212
8. [What is destructuring in JavaScript, and how does it work?](Destructuring-in-JS.md)

0 commit comments

Comments
 (0)