Memoization and the Magic of Self-Containing Functions

How JavaScript Functions Remember Their Past

Memoization and the Magic of Self-Containing Functions

Photo by Ines A. on Unsplash

In JavaScript development, caching data for quick retrieval is paramount. Many developers instinctively reach for objects as key-value stores or arrays for ordered lists. Some might even delve into specialized data structures like Maps and Sets to optimize performance.

However, there's a lesser-known, yet universally available, method that many overlook: functions themselves. Every function in JavaScript has the capability to hold onto data, effectively using itself as a memory bank. 😲

Let's explore how!


Case: Finding the Factorial of a Number βš™οΈ

The factorial of a non-negative integer n is the product of all positive integers less than or equal to n. The factorial is often denoted by n!.

So 6! = 1 x 2 x 3 x 4 x 5 x 6 = 720

An Example with Recursion πŸ”„

Let's look at the function below that uses recursion to calculate the factorial of an input.

Just a reminder: recursion is a programming concept where a function calls itself. Please note that this example does not use tail recursion, which is the more efficient way to use recursion in JavaScript.

function factorial(n) {
    // Input validation
    if (!Number.isInteger(n) || n < 0) {
      return NaN;
    }

    // Base cases
    if (n === 0 || n === 1) {
      return 1;
    }

    // Check if the result has already been cached
    if (n in factorial) {
      return factorial[n];
    }

    // Recursive call and caching
    factorial[n] = n * factorial(n - 1);
    return factorial[n];
}

When Functions Remember πŸ“

Let’s note what happens in this section from the code above:

if (n in factorial) {
    return factorial[n];
}

Memoization

This is an example of memoization - storing the results of function calls and returning the cached result when the same inputs occur again. Normally, to use memoization, you create an array or object to store already computed values. But this function uses itself to store previously computed results!

How cool is that! 😎

Why? Because it can! In JavaScript, functions are objects, therefore you can add properties to them. In this case, previous results of the factorial calculation are stored as properties on the function itself with the input value n as the key.

So when we run factorial(6) the output is 720, and if we print factorial JavaScript shows the function and its properties. The properties are displayed as key-value pairs following the function definition, showing that the function has stored the computed values for future use:

[Function: factorial] { '2': 2, '3': 6, '4': 24, '5': 120, '6': 720 }

Notice the properties that have been added to the function object:

  1. the numbers '2', '3', '4', '5', '6' are the keys, and

  2. their respective values are 2, 6, 24, 120, and 720, representing the factorials of those numbers represented by the keys.

The factorial function is indeed acting as its own cache or container! If you ever doubted the power and flexibility JavaScript has to offer, let this be a moment of enlightenment. πŸ“¦ πŸ’‘

(Ok, Python can do this as well. But this is not a competition, right?)

Resources

Noticed how I like to recommend this book?! 😎

  1. JavaScript: The Definitive Guide

    7th Edition, by David Flanagan

    O'Reilly Media, 2020

  2. MDN Documentation on JavaScript Functions

Blog post originally published on corinamurg.dev.

Β