Memoization Get Certified
Memoization is an optimization technique where the result of an expensive function call is cached. If the function is called again with the same arguments, the cached result is returned instantly instead of redoing the work.
Manual Memoization
What this code does: Manually caches results so repeated calls with the same argument skip the calculation.
const cache = {};
function slowSquare(n) {
if (n in cache) {
console.log("Cache hit for", n);
return cache[n];
}
console.log("Calculating square of", n, "...");
// Simulate some work
let result = n * n;
for (let i = 0; i < 1000000; i++) {
result = n * n;
}
cache[n] = result;
return result;
}
console.log("Result:", slowSquare(5));
console.log("Result:", slowSquare(5));
console.log("Result:", slowSquare(9));
n is already stored, return it and skip the work.Writing a Generic memoize() Helper
What this code does: Builds a reusable memoize() helper that automatically caches any pure function.
function memoize(fn) {
const cache = new Map();
return function (arg) {
if (cache.has(arg)) {
console.log("(from cache)");
return cache.get(arg);
}
const result = fn(arg);
cache.set(arg, result);
return result;
};
}
function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
const memoizedFib = memoize(fibonacci);
console.log("fib(10) =", memoizedFib(10));
console.log("fib(10) again =", memoizedFib(10));
console.log("fib(12) =", memoizedFib(12));
Memoizing Functions With Multiple Arguments
What this code does: Handles multiple arguments by turning the argument list into a single cache key.
function memoize(fn) {
const cache = new Map();
return function (...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
console.log("(from cache) for", key);
return cache.get(key);
}
console.log("Calculating for", key, "...");
const result = fn(...args);
cache.set(key, result);
return result;
};
}
function rectangleArea(width, height) {
return width * height;
}
const memoizedArea = memoize(rectangleArea);
console.log("Area:", memoizedArea(4, 5));
console.log("Area:", memoizedArea(4, 5));
console.log("Area:", memoizedArea(10, 2));
JSON.stringify(args) turns [4,5] into a stable string key.Measuring the Speed Difference
What this code does: Times a memoized call versus a fresh computation to show the performance gain.
function memoize(fn) {
const cache = new Map();
return function (arg) {
if (cache.has(arg)) {
return cache.get(arg);
}
const result = fn(arg);
cache.set(arg, result);
return result;
};
}
function slowCalculation(n) {
let total = 0;
for (let i = 0; i < 20000000; i++) {
total += i % n;
}
return total;
}
const memoizedCalc = memoize(slowCalculation);
const start1 = performance.now();
memoizedCalc(7);
const uncachedTime = performance.now() - start1;
const start2 = performance.now();
memoizedCalc(7);
const cachedTime = performance.now() - start2;
console.log("Uncached call took roughly:", Math.round(uncachedTime), "ms");
console.log("Cached call took roughly:", Math.round(cachedTime), "ms");
When Memoization Doesn't Help
What this code does: Shows why memoizing impure functions (ones that depend on outside state) is dangerous.
function memoize(fn) {
const cache = new Map();
return function (arg) {
if (cache.has(arg)) {
return cache.get(arg);
}
const result = fn(arg);
cache.set(arg, result);
return result;
};
}
// Impure: depends on the current time
function getGreeting(name) {
const hour = new Date().getHours();
return hour < 12 ? "Good morning, " + name : "Good day, " + name;
}
const memoizedGreeting = memoize(getGreeting);
console.log("First call:", memoizedGreeting("Sam"));
console.log("Second call (cached):", memoizedGreeting("Sam"));
console.log("Even if the hour changes later, this cached result won't update.");
new Date() — result can change over time.Memoization at a Glance
| Aspect | Without Memoization | With Memoization |
|---|---|---|
| Repeated call, same arguments | Recomputes every time | Returns cached result instantly |
| Memory usage | Low | Higher (results stored) |
| Safe for | Any function | Pure functions only |
| Best suited for | Cheap or rare calls | Expensive, repeated calls |
Common Mistakes
- Memoizing impure functions (time, randomness, external state)
- Letting the cache grow forever with no size limit
- Using object references as keys without serializing them
- Memoizing cheap functions where the cache overhead is higher than the work
- Ignoring that mutable arguments can change after being cached
Best Practices
- Only memoize pure functions
- Use it for genuinely expensive, frequently repeated work
- Consider a cache size limit or eviction strategy for long-running apps
- Serialize multi-argument or object inputs consistently
- Keep the memoize helper generic and reusable
Quick Summary
- Memoization caches results so repeated calls with the same arguments skip recomputation
- A generic
memoize()helper adds caching without changing the original function - Multiple arguments are usually turned into a single key with
JSON.stringify() - Only works correctly with pure functions
- Trades memory for speed — useful for expensive, repeated work
Try It Yourself
What this code does: Memoizes an isPrime checker and logs cache hits versus actual computations.
function memoize(fn) {
const cache = new Map();
return function (...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
console.log("Cache hit:", key);
return cache.get(key);
}
console.log("Computing for:", key);
const result = fn(...args);
cache.set(key, result);
return result;
};
}
function isPrime(n) {
if (n < 2) return false;
for (let i = 2; i <= Math.sqrt(n); i++) {
if (n % i === 0) return false;
}
return true;
}
const memoizedIsPrime = memoize(isPrime);
console.log("17 is prime:", memoizedIsPrime(17));
console.log("17 is prime:", memoizedIsPrime(17));
console.log("18 is prime:", memoizedIsPrime(18));
Tip: Before memoizing anything, ask: “Is this function pure, and is it actually expensive?” If either answer is no, memoization is usually not worth it.
🧪 Test Your JavaScript Code
Edit the code on the left and click "Run Code" to see the console output on the right.
Click "Run Code" to see the output here.
Code Explanation
The complete example stays visible. Use Previous and Next to move through its logical blocks.
Select Next to begin exploring the code.