Memoization in JavaScript
Tags: javascript, closures, memoization
Published: 2024-04-24 10:11:39

---
title: Memoization in JavaScript
published: true
description: This is a continuation of the conversations about closure. This time I'll talk about memoization, which is a performance optimization technique that leverages closures.
tags: JavaScript, Closures, Memoization
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mr5z1604dtwzqzo8lzz1.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2024-04-23 19:09 +0000
---

Following my previous post about [closures in JavaScript](https://dev.to/diegolepore/a-practical-introduction-to-closures-in-javascript-part-1-1l5k), I wanted to continue the conversation and talk about memoization, which is a performance optimization technique that leverages closures.

Memoization relies on function purity. Pure functions consistently return the same output for identical input arguments. **Memoization enhances efficiency by caching these arguments along with the output they generate. When these arguments are passed again, the function retrieves the result from the cache instead of recalculating it**. This is basically why memoization is ideal only on pure functions, but this will be clearer as I illustrate the concept in the following example.

Consider a basic function to calculate the sum of numbers:

```javascript
function sumNums(...nums) {
  return nums.reduce((total, num) => total + num, 0);
}

console.log(sumNums(1, 2, 3)); // Output: 6
```

Now, imagine that instead of a straightforward operation like summing three numbers, we're dealing with a more complex function that uses recursion and challenging computations. In such cases, memoization is particularly valuable.

In our example, memoization optimizes the function by **ensuring that the arithmetic operation is performed just once**. This is achieved by storing `1, 2, 3` as the `key` and `6` as the `value` in an object that acts as the function’s cache. In my demonstration, I'll use an object for the cache, but you can choose any data structure that best suits your specific needs and preferences.

Let's define a memo function that takes another function and memoizes it, and then let's memoize our `sumNums` function:

```javascript
/**
 * Memoizes a function to cache its results based on the arguments provided.
 * If the result for a given set of arguments is already in the cache, it retrieves it from there.
 * Otherwise, it calculates the result, stores it in the cache, and returns it.
 *
 * @param {Function} fn - The function to be memoize.
 * @returns {Function} The memoize function.
 */
function memo(fn) {
  const cache = {};

  return (...args) => {
    // Generate a unique key based on arguments to store function results
    const key = JSON.stringify(args);

    // Check if the result is already cached
    if (cache[key]) {
      return cache[key];
    }

    // Calculate the result and cache it if it's not already cached
    const result = fn(...args);
    cache[key] = result;
    return result;
  };
}

// Create a memoize version of the sumNums function
const memoizedSumNums = memo(sumNums);

console.log(memoizedSumNums(1, 2, 3)); 
// Calculates and returns 6

console.log(memoizedSumNums(1, 2, 3)); 
// Fetches and returns 6 from cache without recalculating

```

I encourage you to try it out by memoizing different functions. For instance, you could try memoizing a fibbonacci function. And then you can compare how fast it runs with and without memoizing it.

To wrap up, memoization is a powerful technique for boosting the efficiency of pure functions by avoiding unnecessary recalculations, and enhancing performance. However, while memoization can significantly cut down on processing time, especially in scenarios involving complex tasks, it does so at the cost of increased memory usage. 

As results are cached, memory consumption can grow, depending on the number and size of the cached results. This trade-off needs careful consideration, particularly in environments where memory is limited. 

Opt for memoization when the benefits of reduced computation outweigh the costs of additional memory usage. This way, your functions not only run faster but also remain efficient in their use of system resources, keeping your code optimized and maintainable.