Academic Block

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));

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));

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.");

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.

📝 JavaScript Code
👁 Console Output

Click "Run Code" to see the output here.

Ctrl + Enter to run code Esc to close