How _.memoize Caches Function Results in Lodash
This article explains how the _.memoize method in the
Lodash JavaScript library stores and retrieves the results of
computationally expensive functions. You will learn the mechanics behind
its internal caching system, how it generates cache keys from function
arguments, how to handle multiple parameters using custom resolvers, and
how to inspect or clear the underlying cache to optimize application
performance.
What is _.memoize?
In JavaScript, memoization is an optimization technique used to speed
up programs by storing the results of expensive function calls and
returning the cached result when the same inputs occur again. Lodash
provides this capability out of the box through
_.memoize(func, [resolver]).
When you wrap a function with _.memoize, it returns a
new memoized version of that function. This wrapper intercepts calls,
checks an internal storage map for existing results, and avoids
re-running the original computation whenever possible.
The Internal Cache Mechanism
Under the hood, _.memoize maintains a cache instance
directly on the returned function as a property called
.cache.
By default, Lodash creates this cache using a constructor compatible
with the JavaScript Map interface. It relies on standard
key-value operations:
- Lookup: Before invoking the wrapped function,
Lodash checks if the computed key exists using
.has(key). - Retrieval: If the key exists (a cache hit), it
retrieves the stored result using
.get(key)and returns it immediately. - Storage: If the key does not exist (a cache miss),
it invokes the original function with the supplied arguments, saves the
output using
.set(key, value), and returns the newly computed value.
By default, the cache is an instance of _.memoize.Cache,
which defaults to the internal MapCache (or the native
Map in modern environments).
Default Key Resolution
By default, _.memoize determines the cache key using
only the first argument passed to the function:
const computeSquare = _.memoize((n) => {
// Expensive calculation
return n * n;
});
computeSquare(4); // Computes and stores with key: 4
computeSquare(4); // Cache hit, returns stored result: 16Because Lodash uses the first argument as the key, calls with multiple arguments can lead to unexpected behavior if not configured properly:
const add = _.memoize((a, b) => a + b);
add(2, 3); // Computes 5 (cached under key: 2)
add(2, 10); // Returns 5! (Cache hit because key '2' already exists)Using a Custom Resolver for Multiple Arguments
To cache results based on multiple arguments or complex object
properties, _.memoize accepts an optional second parameter:
a resolver function. The resolver receives the same
arguments as the memoized function and must return a value to be used as
the cache key.
const add = _.memoize(
(a, b) => a + b,
(a, b) => `${a}_${b}` // Custom key resolver
);
add(2, 3); // Computes 5 (cached under key: "2_3")
add(2, 10); // Computes 12 (cached under key: "2_10")For objects or arrays, you can use serialization (like
JSON.stringify) within the resolver, although this adds
slight overhead:
const processData = _.memoize(
(config) => runHeavyTask(config),
(config) => JSON.stringify(config)
);Inspecting and Manipulating the Cache
Because the cache is exposed on the function itself, you can directly inspect, modify, or clear stored computations:
const memoized = _.memoize(expensiveOperation);
// View cache size or keys
console.log(memoized.cache.size);
// Check if a result is cached
if (memoized.cache.has(key)) {
console.log("Cached result:", memoized.cache.get(key));
}
// Clear a specific entry
memoized.cache.delete(key);
// Clear the entire cache
memoized.cache.clear();You can also replace _.memoize.Cache globally or on an
individual function instance if your application requires a custom
storage engine, such as an LRU (Least Recently Used) cache to prevent
memory leaks from unbounded growth.