Memoization and the Magic of Self-Containing Functions
How JavaScript Functions Remember Their Past
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:
the numbers '2', '3', '4', '5', '6' are the keys, and
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?! π
JavaScript: The Definitive Guide
7th Edition, by David Flanagan
O'Reilly Media, 2020
Blog post originally published on corinamurg.dev.