![]() |
When working with complex or repetitive functions in JavaScript, optimizing performance becomes crucial. In this article, you'll learn what memoization is, how it works, and why it's such an effective technique for improving the speed of your applications.
What is Memoization?
Memoization is an optimization technique that consists of storing the results of expensive function calls so that subsequent invocations with the same arguments can immediately return the previously calculated value, avoiding unnecessary recalculations.
In practice, memoizing a function means adding a cache that saves the results based on the parameters received. If a function is called multiple times with the same arguments, the value is read from the cache instead of re-executing the entire function.
Why Use Memoization?
The main situations where memoization is useful include:
- Heavy recursive mathematical calculations (e.g., Fibonacci, factorials).
- Pure functions with limited and repeated inputs.
- Complex rendering in React environments or animations.
- Transformations on immutable data or large datasets.
Key Benefits:
- ✅ Improved Performance: Reduces the number of redundant executions.
- ✅ Immediate response: Improves application responsiveness.
- ✅ Load reduction: Optimizes CPU usage, useful for CPU-bound functions.
- ✅ Ideal for deterministic functions: If a function always returns the same output for a given input, memoizing it is safe and effective.
How does memoization work in JavaScript?
Let's look at a practical example of a function without memoization and then an optimized version.
Function without memoization
function slowFibonacci(n) {
if (n <= 1) return n;
return slowFibonacci(n - 1) + slowFibonacci(n - 2);
}
console.log(slowFibonacci(40)); // Extremely slow
This function is a classic example of exponential recursion, where each call generates two new calls. The execution time increases very quickly.
Memoized version
function memoizedFibonacci() {
const cache = {};
return function fib(n) {
if (n in cache) {
return cache[n];
}
if (n <= 1) return n;
cache[n] = fib(n - 1) + fib(n - 2);
return cache[n];
};
}
const fibonacci = memoizedFibonacci();
console.log(fibonacci(40)); // Much faster
In this version, the fib
function saves its results in a cache (cache[n]
). Each result is calculated only once and then reused.
Example of Memoization with API Calls
Memoization can also be useful to avoid redundant API calls. If a given API request always returns the same result for the same parameters, we can save the result in the cache and reuse it in subsequent calls.
function memoizeApiCall(apiFunction) {
const cache = new Map();
return async function (...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key); // return from cache
}
const result = await apiFunction(...args);
cache.set(key, result);
return result;
};
}
// Example API call
async function fetchUserData(userId) {
const response = await fetch(`https://jsonplaceholder.typicode.com/users/${userId}`);
if (!response.ok) throw new Error("Error in the request");
return response.json();
}
// Stored version
const memoizedFetchUserData = memoizeApiCall(fetchUserData);
// Usage
memoizedFetchUserData(1).then(data => console.log("First call:", data));
memoizedFetchUserData(1).then(data => console.log("Second call from cache:", data));
In this example, memoizeApiCall
wraps an asynchronous API function and saves the result for each set of parameters (here, the userId
). The second call with the same ID does not generate a new HTTP request, but immediately returns the result from the cache.
⚠️ Note: The cache is in memory, so it resets when the page is reloaded. For persistent caches you might consider localStorage
or IndexedDB.
Implement a Generic Memoization Function
Memoization can be easily generalized in JavaScript for any function with deterministic inputs:
function memoize(fn) {
const cache = new Map();
return function (...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = fn.apply(this, args);
cache.set(key, result);
return result;
};
}
// Example of use
function add(a, b) {
console.log("Computing...");
return a + b;
}
const memoizedAdd = memoize(add);
console.log(memoizedAdd(3, 4)); // Computing... 7
console.log(memoizedAdd(3, 4)); // From cache: 7
This version uses Map
and JSON.stringify
to create a unique key based on arguments. It's a very common and reusable technique.
Memoization and Non-Pure Functions
⚠️ It's important to remember that memoization works only with pure functions, that is:
- Same output for the same inputs.
- No side effects.
Functions that depend on global state, input/output, or interactions with the environment are not suitable for memoization, because caching may produce outdated or incorrect results.
Memoization in modern environments (React, Lodash, etc.)
In React
In the React world, memoization is used for:
- Avoid expensive recalculations (with
useMemo
). - Avoid function reconstructions (with
useCallback
). - Avoid unnecessary renderings (with
React.memo
).
const memoizedValue = useMemo(() => expensiveFunction(input), [input]);
Lodash
The Lodash library includes a function _.memoize()
:
const _ = require('lodash');
const memoizedFn = _.memoize(someExpensiveFunction);
Lodash manages the cache internally and is useful for more complex applications or production.
Limitations and Considerations
- Memory Usage: Each result is cached; If the inputs are many or unique, the cache can grow significantly.
- Cache invalidation: It's not easy to manage when to expire or refresh the cache.
- Complex arguments:
JSON.stringify
has limitations with cyclic objects or functions as arguments. - Space/time trade-off: Performance gains come at the cost of memory usage.
Conclusions
Memoization is a powerful optimization technique for deterministic functions, especially those that are expensive to execute repeatedly. In JavaScript, it's simple to implement and very useful in modern web applications, especially when combined with React or libraries like Lodash.
💡 Best Practice: Only cache pure functions, use off-the-shelf tools when available (React, Lodash), and keep an eye on cache management.
Follow me #techelopment
Official site: www.techelopment.it
facebook: Techelopment
instagram: @techelopment
X: techelopment
Bluesky: @techelopment
whatsapp: Techelopment
youtube: @techelopment