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:

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: 16

Because 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.