Skip to Content
Course content

112: Memoization Techniques

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Imagine you're a chef at a high-end restaurant. You have a signature sauce that takes four hours of simmering, reducing, and careful skimming to get just right. Now, you could start that process from scratch every single time a customer orders the dish, but you'd be out of business in a week—the customers would be waiting half a day for their dinner.

Instead, you make a massive batch in the morning and keep it in a chilled container in the fridge. When an order comes in, you don't go back to the stove; you check the fridge. If the sauce is there, you pour it out instantly. If you've run out, only then do you put in the grueling work to make more. That's memoization in a nutshell.

In JavaScript, we map this logic directly to our functions:

  • The four-hour process is your "expensive" function (something that eats CPU or takes a long time).
  • The fridge is a cache object or a Map stored in memory.
  • Checking the fridge is looking up the function's arguments as keys in that object.
  • Pouring the sauce is returning the cached value without re-running the logic.

Building Your Own Cache Layer

I usually prefer building a generic memoization wrapper rather than hard-coding caches into every single function. It keeps the logic clean. Here is how I’d implement a basic version that can wrap any function.

const memoize = (fn) => {
  const cache = new Map();

  return (...args) => {
    // We create a key based on the arguments
    const key = JSON.stringify(args);

    if (cache.has(key)) {
      console.log('Fetching from cache...');
      return cache.get(key);
    }

    console.log('Calculating result...');
    const result = fn(...args);
    cache.set(key, result);
    return result;
  };
};

// Let's simulate a "heavy" operation
const expensiveCalculation = (num) => {
  let i = 0;
  while (i < 1000000000) i++; // Artificial delay
  return num * 2;
};

const memoizedCalc = memoize(expensiveCalculation);

console.log(memoizedCalc(10)); // Calculating result... 20
console.log(memoizedCalc(10)); // Fetching from cache... 20

Notice that I used JSON.stringify(args). Since the cache is a Map, and JavaScript compares objects by reference, not by value, we need a way to turn the arguments into a unique string key. It's a quick-and-dirty way to handle multiple arguments. Just be careful: if you're passing massive objects as arguments, stringifying them can actually become the new bottleneck.

When This Actually Becomes a Burden

Now, it's tempting to memoize everything, but I've seen developers shoot themselves in the foot by doing this. Memoization is a trade-off: you are trading memory for speed.

If your function is called with thousands of different arguments, your cache object will grow indefinitely, potentially leading to a memory leak. If the function is cheap to run—like adding two numbers—the overhead of checking the cache is actually slower than just doing the math. I only reach for this technique when the function is "pure" (meaning the same input always yields the same output) and the computation is noticeably heavy.

Handling Recursive Depth

The real magic happens with recursion. If you've ever tried to calculate a Fibonacci sequence without memoization, you know it hits a wall very quickly because it recalculates the same numbers millions of times. By memoizing the recursive call, you collapse an exponential time complexity into linear time. It's the difference between a browser tab freezing and getting an answer in 2 milliseconds.




📋 Practical Task

Building a Memoized Prime Factorization Engine

Your task is to create a function that finds the prime factors of a given number. Because calculating prime factors for very large numbers can be intensive, you need to implement a memoization layer to ensure that if the same number is checked twice, the result is returned instantly.

Requirements:

  • Create a function getPrimeFactors(n) that returns an array of prime factors for the number n.
  • Wrap this function using a memoization technique (either a wrapper function or an internal cache).
  • Test it by calling the function with a large number (e.g., 123456789) twice. The first call should take a moment; the second call should be nearly instantaneous.
  • Log a message to the console whenever a result is retrieved from the cache versus when it is calculated from scratch.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.