Skip to main content
Practice Problems

Memoization in JavaScript

What is Memoization?

Memoization is an optimization technique that stores (caches) the results of function execution for specific arguments. On subsequent calls with the same arguments, the function returns the cached result instead of recomputing.

In Simple Terms

Memoization is "remembering" the results of expensive computations so you don't have to redo them.


Why is Memoization Needed?

  • Optimize performance of expensive computations
  • Reduce repeated calculations
  • Speed up recursive algorithms
  • Optimize rendering in React

Basic Implementation

javascript
function memoize(fn) { const cache = {}; // Object to store results return function(...args) { const key = JSON.stringify(args); // Key from arguments if (key in cache) { console.log('From cache'); return cache[key]; } console.log('Computing'); const result = fn(...args); cache[key] = result; return result; }; } // Usage function expensiveSum(a, b) { // Simulate long operation for (let i = 0; i < 1000000000; i++) {} return a + b; } const memoizedSum = memoize(expensiveSum); console.time('First call'); memoizedSum(5, 10); // Computing console.timeEnd('First call'); // ~1000ms console.time('Second call'); memoizedSum(5, 10); // From cache console.timeEnd('Second call'); // ~0ms

Classic Example: Fibonacci Numbers

Without Memoization (very slow)

javascript
function fibonacci(n) { if (n <= 1) return n; return fibonacci(n - 1) + fibonacci(n - 2); } console.time('fib'); fibonacci(40); // ~1.5 seconds console.timeEnd('fib');

Problem: Function computes the same values many times.

fib(5)

fib(4)

fib(3)

fib(3)

fib(2)

fib(2)

fib(2)

fib(3) computed 2 times, fib(2) — 3 times!

With Memoization (fast)

javascript
const fibonacci = memoize((n) => { if (n <= 1) return n; return fibonacci(n - 1) + fibonacci(n - 2); }); console.time('fib'); fibonacci(40); // ~0.5 milliseconds console.timeEnd('fib');

Result:

Speed increases by thousands of times for large N values.


Advanced Implementation

With Cache Size Limit

javascript
function memoize(fn, maxSize = 100) { const cache = new Map(); return function(...args) { const key = JSON.stringify(args); if (cache.has(key)) { return cache.get(key); } const result = fn(...args); // Limit cache size if (cache.size >= maxSize) { const firstKey = cache.keys().next().value; cache.delete(firstKey); // Remove oldest } cache.set(key, result); return result; }; }

With TTL (Time To Live)

javascript
function memoizeWithTTL(fn, ttl = 5000) { const cache = new Map(); return function(...args) { const key = JSON.stringify(args); const cached = cache.get(key); if (cached && Date.now() - cached.timestamp < ttl) { return cached.value; } const result = fn(...args); cache.set(key, { value: result, timestamp: Date.now() }); return result; }; } // Usage const fetchUser = memoizeWithTTL(async (id) => { const response = await fetch(`/api/users/${id}`); return response.json(); }, 10000); // Cache for 10 seconds

With LRU (Least Recently Used)

javascript
function memoizeLRU(fn, maxSize = 100) { const cache = new Map(); return function(...args) { const key = JSON.stringify(args); if (cache.has(key)) { const value = cache.get(key); // Move to end (mark as recently used) cache.delete(key); cache.set(key, value); return value; } const result = fn(...args); cache.set(key, result); // Remove oldest (first) element if (cache.size > maxSize) { const firstKey = cache.keys().next().value; cache.delete(firstKey); } return result; }; }

Memoization in React

React.memo

Memoizes entire component — skips re-render if props haven't changed.

javascript
import { memo } from 'react'; const ExpensiveComponent = memo(({ data }) => { console.log('Rendering ExpensiveComponent'); // Heavy computations const processed = processData(data); return <div>{processed}</div>; }); // Component re-renders only if data changed

With Custom Comparison

javascript
const UserCard = memo( ({ user }) => { return <div>{user.name}</div>; }, (prevProps, nextProps) => { // Return true if should NOT update return prevProps.user.id === nextProps.user.id; } );

useMemo

Memoizes computation result inside component.

javascript
import { useMemo } from 'react'; function ProductList({ products, filterText }) { const filteredProducts = useMemo(() => { console.log('Filtering products'); return products.filter(p => p.name.toLowerCase().includes(filterText.toLowerCase()) ); }, [products, filterText]); return ( <ul> {filteredProducts.map(p => ( <li key={p.id}>{p.name}</li> ))} </ul> ); }

Important:

useMemo recalculates value only when dependencies in array [products, filterText] change.

useCallback

Memoizes the function itself (to avoid creating new function on each render).

javascript
import { useCallback, memo } from 'react'; const Button = memo(({ onClick, children }) => { console.log(`Rendering button "${children}"`); return <button onClick={onClick}>{children}</button>; }); function Parent() { const [count, setCount] = useState(0); const [other, setOther] = useState(0); // Creates new function on each render const handleClick = () => setCount(count + 1); // Function created once const handleClickMemo = useCallback(() => { setCount(prev => prev + 1); }, []); return ( <> <Button onClick={handleClickMemo}>Count: {count}</Button> <button onClick={() => setOther(other + 1)}>Other: {other}</button> </> ); }

When NOT to Use Memoization?

For Simple Computations

javascript
// Not needed const doubled = useMemo(() => count * 2, [count]); // Better just const doubled = count * 2;

For Functions with Side Effects

javascript
// Bad - API call will be cached forever const fetchData = memoize(async (id) => { return await fetch(`/api/users/${id}`); });

When Cache Takes More Memory Than It Saves Time

javascript
// If function called rarely with different arguments const memoizedSort = memoize(arr => [...arr].sort()); // Cache will grow infinitely

Libraries for Memoization

Lodash

javascript
import { memoize } from 'lodash'; const expensiveFn = memoize((a, b) => { return a + b; }); // Can set custom resolver for key const memoized = memoize( (obj) => obj.value, (obj) => obj.id // Cache key );

fast-memoize

javascript
import memoize from 'fast-memoize'; const fn = memoize((a, b) => a + b);

reselect (for Redux)

javascript
import { createSelector } from 'reselect'; const getUsers = state => state.users; const getFilter = state => state.filter; const getFilteredUsers = createSelector( [getUsers, getFilter], (users, filter) => users.filter(u => u.name.includes(filter)) ); // Result is cached

Pitfalls

1. Argument Serialization

javascript
// Objects with same data but different references const obj1 = { id: 1 }; const obj2 = { id: 1 }; JSON.stringify(obj1) === JSON.stringify(obj2); // true, but slow // Better to use primitives as keys function memoize(fn) { const cache = new Map(); return function(id) { // Only primitive argument if (cache.has(id)) return cache.get(id); const result = fn(id); cache.set(id, result); return result; }; }

2. Memory Leaks

javascript
// Cache grows infinitely const memoized = memoize(expensiveFn); // Limit size or add TTL const memoized = memoizeLRU(expensiveFn, 100);

3. Mutations in React

javascript
// useMemo won't work with object mutation const obj = { count: 0 }; obj.count++; // Mutation - same reference // Create new object setObj({ ...obj, count: obj.count + 1 });

Conclusion

Memoization:

  • Optimizes expensive computations
  • Critical for recursive algorithms
  • Important for React application performance
  • Not always needed — measure performance
  • Can lead to memory leaks without cache size control
  • Requires proper dependency management

In Interviews:

Important to be able to:

  • Explain what memoization is and why it's needed
  • Implement basic memoize function
  • Provide examples (Fibonacci, API requests)
  • Explain difference between useMemo, useCallback, and React.memo
  • Understand when memoization is not needed or harmful

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?
Practice Problems