Memoization is a fancy name for a plain idea: if a function is going to be asked the same question repeatedly, remember the answer the first time and hand back the stored copy after that. It trades memory for speed, and when the function is expensive and gets called with repeating inputs, the trade is a bargain.
What changed in 2026
- Framework-level memoization matured. UI frameworks now lean on compiler-driven memoization to skip re-renders automatically, reducing the amount of hand-written caching developers do.
- Bounded caches became the default advice. After years of unbounded memoization causing memory leaks, LRU and TTL-bounded caches are now the recommended baseline rather than an optimization afterthought.
- Purity tooling improved. Linters and type systems are better at flagging functions with hidden side effects, catching the number-one cause of subtly wrong memoization.
How memoization works
At its core, memoization wraps a function with a lookup table:
function memoize(fn) {
const cache = new Map();
return (n) => {
if (cache.has(n)) return cache.get(n);
const result = fn(n);
cache.set(n, result);
return result;
};
}
The first call with a given argument computes and stores. Every later call with the same argument is a cheap map lookup. The classic demo is Fibonacci: naive recursion recomputes the same values exponentially many times, while a memoized version computes each value once and turns an exponential algorithm into a linear one.
The one hard rule: pure functions only
Memoization is only correct when the function is pure — its output depends solely on its inputs, and it has no side effects. If a function reads a database, checks the clock, or mutates global state, a cached result becomes a stale lie the moment the outside world changes.
This is where memoization and general caching diverge: memoization assumes the mapping from input to output never changes, so it needs no invalidation. General caching of impure operations does need invalidation, and that is a genuinely hard problem.
Memoization vs related techniques
| Technique |
Caches |
Needs invalidation? |
Typical scope |
| Memoization |
Pure function results by args |
No (inputs fully determine output) |
In-process, one function |
| General caching |
Any expensive/IO result |
Yes |
Cross-request, shared |
| Dynamic programming (top-down) |
Overlapping subproblem results |
No |
One algorithm run |
| Materialized view |
Query results |
Yes, on data change |
Database |
Top-down dynamic programming is essentially memoized recursion — the same mechanism, applied to overlapping subproblems.
When to use it, and when not to
Use memoization when all of these hold: the function is pure, it is genuinely expensive, and it gets called repeatedly with a limited set of repeating inputs. A recursive parser, a layout calculation, or an expensive derivation over stable data are good fits.
Avoid it when the function is already cheap (the map lookup can cost more than the computation), when the input space is huge and rarely repeats (your cache fills with entries used once), or when correctness depends on fresh data. If you are weighing recompute against store, the same instincts apply as in recursion vs iteration — sometimes the straightforward computation wins.
Common pitfalls
Unbounded caches. A memoize wrapper with no size limit is a memory leak with extra steps. Use an LRU or TTL bound for anything long-lived.
Bad cache keys. If arguments are objects, naive keys (reference identity, or sloppy serialization) either miss constantly or collide dangerously. Key on a stable, value-based representation.
Memoizing impure functions. The subtle killer. It works in testing, then returns stale data in production when the underlying state changes.
Premature memoization. Adding caches before profiling. You obscure the code and may slow it down. Measure first.
FAQ
Is memoization the same as caching?
Memoization is a specific kind of caching: caching the return value of a pure function keyed on its arguments. All memoization is caching, but not all caching is memoization — caching impure or IO results is a broader, harder problem.
Does memoization use a lot of memory?
It can. That is the whole trade — memory for speed. Bounded caches (LRU, TTL) keep the memory cost predictable at the price of occasional recomputation.
Is memoization the same as dynamic programming?
Top-down dynamic programming is memoized recursion. Bottom-up DP fills a table iteratively instead. Both avoid recomputing overlapping subproblems; they differ in direction and control flow.
Can I memoize an async function?
Yes, but cache the promise, not just the resolved value, so concurrent callers share one in-flight computation instead of triggering duplicate work.
Where to go next