Memoization caches a function's previous results so identical calls can return instantly instead of recomputing. It is one of the most direct practical payoffs of writing pure functions.
1Memoization | JavaScript Tutorial - In-Depth Guide Part 1
Memoization caches the result of a function call keyed by its arguments, so a repeated call with the same arguments returns instantly from cache.
const cache = new Map();
function slowSquare(n) {
if (cache.has(n)) return cache.get(n);
const result = n * n; // pretend this is expensive
cache.set(n, result);
return result;
}Caching by Input
2Memoization | JavaScript Tutorial - In-Depth Guide Part 2
A generic 'memoize' higher-order function wraps any pure function with this caching behavior, without modifying the original function.
function memoize(fn) {
const cache = new Map();
return (arg) => {
if (cache.has(arg)) return cache.get(arg);
const result = fn(arg);
cache.set(arg, result);
return result;
};
}Generic memoize()
3Memoization | JavaScript Tutorial - In-Depth Guide Part 3
Memoizing a function with multiple arguments requires building a single cache key from all of them, often by serializing the argument list.
function memoize(fn) {
const cache = new Map();
return (...args) => {
const key = JSON.stringify(args);
if (cache.has(key)) return cache.get(key);
const result = fn(...args);
cache.set(key, result);
return result;
};
}Multi-Argument Keys
4Memoization | JavaScript Tutorial - In-Depth Guide Part 4
The classic showcase for memoization is recursive Fibonacci, where naive recursion recomputes the same sub-problems exponentially many times.
const fib = memoize((n) => n <= 1 ? n : fib(n - 1) + fib(n - 2));
fib(40); // fast, thanks to cachingFibonacci Example
5Memoization | JavaScript Tutorial - In-Depth Guide Part 5
Memoization is not free: it trades memory for speed, and an unbounded cache can leak memory in a long-running process if inputs vary widely.
// Prefer a bounded cache in long-running processes:
const cache = new LRUCache({ max: 500 });Memory Trade-off
6Step-by-Step Breakdown
Memoization caches the result of a function call keyed by its arguments, so a repeated call with the same arguments returns instantly from cache.
Checkpoint: Is memoization safe to apply to an impure function whose output can vary for the same input?
- →Yes, memoization works on any function
- →No, it assumes same input always yields the same output
A generic 'memoize' higher-order function wraps any pure function with this caching behavior, without modifying the original function.
Memoizing a function with multiple arguments requires building a single cache key from all of them, often by serializing the argument list.
The classic showcase for memoization is recursive Fibonacci, where naive recursion recomputes the same sub-problems exponentially many times.
Memoization is not free: it trades memory for speed, and an unbounded cache can leak memory in a long-running process if inputs vary widely.
Checkpoint: Can an unbounded memoization cache cause a memory leak in a long-running process?
- →Yes, if inputs vary widely and are never evicted
- →No, JavaScript automatically limits cache size
Next, we'll explore 'Functional Programming Basics'.
Level Up 🚀
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
Fully supported.
Fully supported.
Fully supported.
Fully supported.
Accessibility (A11y)
1Memoized Computations Should Not Delay Live Region Announcements
When memoizing a function that produces text for an ARIA live region, ensure cache hits still trigger the DOM update needed for screen readers to announce the change — a cached value returned without updating the DOM will silently skip the announcement.
SEO Implications
- 1
Memoized Server-Side Rendering Computations Can Improve Time to First Byte
Caching expensive, repeatable computations (like formatting or data transformation) during server-side rendering reduces response latency, which contributes positively to Core Web Vitals and therefore search ranking signals.
Best Practices
Only Memoize Pure Functions
Memoizing an impure function silently returns stale results after the underlying state it depends on changes, producing bugs that are very hard to trace back to the cache.
Bound Cache Size in Long-Running Processes
Use an LRU or time-based eviction strategy instead of an unbounded Map when the range of possible inputs is large or unpredictable, to avoid unbounded memory growth.
Frequent Bugs
Memoizing a function that depends on external mutable state (like the current time or a global config) causes it to return outdated results forever after the first call.
Either make the function pure by passing all relevant state in as explicit arguments, or avoid memoizing it and accept the recomputation cost.
Using default `JSON.stringify` as a cache key for arguments containing functions or undefined values, which get silently dropped or produce inconsistent keys.
For non-trivial argument shapes, use a more robust serialization strategy, or restrict memoization to functions with simple, primitive arguments.
Real-World Examples
Memoizing an Expensive Layout Calculation
A data visualization component recomputed complex chart layout geometry on every render, even when the underlying dataset had not changed.
const computeLayout = memoize((dataset) => {
// expensive geometry calculations
return layoutResult;
});