What is Memoization in JavaScript?
Memoization is an optimization technique used to enhance performance by storing the results of expensive function calls and returning the cached result when the same inputs occur again. It helps in reducing redundant calculations, making functions faster, especially in scenarios like recursion or repetitive computations.
In JavaScript, memoization is commonly implemented using a cache (object or Map) where previously computed values are stored. When the function is called with a known input, the stored value is returned instead of recomputing the result. This technique is widely used in dynamic programming, mathematical computations, and React hooks (e.g., useMemo and useCallback).
Why Use Memoization?
Memoization is useful in scenarios where:
- Expensive computations: It helps optimize functions that involve heavy calculations, such as Fibonacci sequences, recursive algorithms, or large dataset processing.
- Repeated function calls with the same inputs: If a function is called multiple times with the same arguments, memoization ensures the previously computed result is returned instantly instead of recalculating it.
- Performance improvement: By avoiding redundant calculations, memoization significantly enhances efficiency, making JavaScript applications faster and reducing unnecessary function executions.
Example of Memoization
🚀 Fibonacci Sequence (Without vs. With Memoization)
// ❌ Without Memoization (Inefficient)
const fibonacciWithoutMemo = (n) => {
if (n <= 1) return n;
return fibonacciWithoutMemo(n - 1) + fibonacciWithoutMemo(n - 2);
};
console.log(fibonacciWithoutMemo(10)); // Output: 55
// ✅ With Memoization (Optimized)
const fibonacciWithMemo = (n, memo = {}) => {
if (n in memo) return memo[n];
if (n <= 1) return n;
memo[n] = fibonacciWithMemo(n - 1, memo) + fibonacciWithMemo(n - 2, memo);
return memo[n];
};
console.log(fibonacciWithMemo(10)); // Output: 55 (Faster)
🚀 Factorial Calculation (Without vs. With Memoization)
// ❌ Without Memoization
const factorialWithoutMemo = (n) => {
if (n === 0) return 1;
return n * factorialWithoutMemo(n - 1);
};
console.log(factorialWithoutMemo(5)); // Output: 120
// ✅ With Memoization
const factorialWithMemo = (n, memo = {}) => {
if (n in memo) return memo[n];
if (n === 0) return 1;
memo[n] = n * factorialWithMemo(n - 1, memo);
return memo[n];
};
console.log(factorialWithMemo(5)); // Output: 120 (Faster)
Sum of Digits (Without vs. With Memoization)
// ❌ Without Memoization
const sumOfDigitsWithoutMemo = (n) => {
if (n === 0) return 0;
return (n % 10) + sumOfDigitsWithoutMemo(Math.floor(n / 10));
};
console.log(sumOfDigitsWithoutMemo(12345)); // Output: 15
// ✅ With Memoization
const sumOfDigitsWithMemo = (n, memo = {}) => {
if (n in memo) return memo[n];
if (n === 0) return 0;
memo[n] = (n % 10) + sumOfDigitsWithMemo(Math.floor(n / 10), memo);
return memo[n];
};
console.log(sumOfDigitsWithMemo(12345)); // Output: 15 (Faster)
In all these problems, we store the result of the same input to avoid redundant calculations, making our functions more efficient and improving performance significantly.