lodash.memoize provides a memoization function that caches the results of expensive function calls and returns the cached result when the same inputs occur again. It implements an efficient caching mechanism using MapCache with automatic fallbacks for different environments and key types.
npm install lodash.memoizeconst memoize = require('lodash.memoize');For ES6 modules (if using a bundler):
import memoize from 'lodash.memoize';const memoize = require('lodash.memoize');
// Memoize an expensive calculation
const fibonacci = memoize(function(n) {
if (n < 2) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
});
// First call - executes the function
console.log(fibonacci(10)); // 55 (calculated)
// Second call - returns cached result
console.log(fibonacci(10)); // 55 (from cache)
// Custom cache key resolver
const getUser = memoize(function(id) {
return database.fetchUser(id);
}, function(id) {
return `user_${id}`;
});Creates a function that memoizes the result of func. If resolver is provided, it determines the cache key for storing the result based on the arguments provided to the memoized function.
/**
* Creates a function that memoizes the result of `func`.
* @param {Function} func - The function to have its output memoized
* @param {Function} [resolver] - The function to resolve the cache key
* @returns {Function} Returns the new memoized function
* @throws {TypeError} Throws if `func` is not a function or `resolver` is provided but not a function
*/
function memoize(func, resolver);Parameters:
func (Function, required): The function to have its output memoizedresolver (Function, optional): The function to resolve the cache key. If not provided, the first argument to the memoized function is used as the cache keyReturns: Function - The new memoized function with a cache property
Throws: TypeError - If func is not a function or resolver is provided but not a function
Usage Examples:
// Basic memoization
const expensiveOperation = memoize(function(n) {
console.log('Computing...');
return n * n * n;
});
expensiveOperation(5); // Logs 'Computing...' and returns 125
expensiveOperation(5); // Returns 125 from cache (no log)
// Custom resolver for complex cache keys
const getUserData = memoize(function(userId, includeProfile) {
return api.fetchUser(userId, includeProfile);
}, function(userId, includeProfile) {
return `${userId}_${includeProfile}`;
});
// Manual cache manipulation
const memoizedFunc = memoize(someFunction);
memoizedFunc.cache.set('key', 'value'); // Set cache value
memoizedFunc.cache.get('key'); // Get cache value
memoizedFunc.cache.has('key'); // Check cache key
memoizedFunc.cache.delete('key'); // Remove cache entryThe memoized function exposes its cache as the cache property, allowing for manual cache manipulation.
/**
* Cache instance attached to memoized functions
* @type {MapCache}
*/
memoizedFunction.cache;Methods available on cache:
clear(): Removes all cached entriesdelete(key): Removes a specific cache entry, returns booleanget(key): Gets a cached value by keyhas(key): Checks if a key exists in cache, returns booleanset(key, value): Sets a cache entry, returns the cache instanceThe default cache constructor used by memoize. Can be replaced with custom cache implementations.
/**
* Default cache constructor for memoize
* @type {Function}
*/
memoize.Cache;Usage:
// Use WeakMap as cache (for object keys only)
memoize.Cache = WeakMap;
const memoizedFunc = memoize(function(obj) {
return obj.value * 2;
});
// Use custom cache implementation
memoize.Cache = class CustomCache {
constructor() {
this.data = new Map();
}
get(key) { return this.data.get(key); }
set(key, value) { this.data.set(key, value); return this; }
has(key) { return this.data.has(key); }
delete(key) { return this.data.delete(key); }
clear() { this.data.clear(); }
};/**
* MapCache - Default cache implementation
* Provides efficient caching with automatic fallbacks for different key types
*/
class MapCache {
constructor(entries);
clear();
delete(key);
get(key);
has(key);
set(key, value);
}
/**
* Memoized function type with cache property
*/
interface MemoizedFunction {
(...args: any[]): any;
cache: MapCache;
}// For recursive functions
const fibonacci = memoize(function(n) {
if (n < 2) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
});
// For API calls with complex parameters
const fetchData = memoize(function(params) {
return fetch('/api/data', { method: 'POST', body: JSON.stringify(params) });
}, function(params) {
return JSON.stringify(params); // Serialize complex objects for cache key
});const memoizedFunc = memoize(expensiveFunction);
// Check cache size (not directly available, but can be estimated)
console.log(memoizedFunc.cache);
// Clear cache when needed
memoizedFunc.cache.clear();
// Selective cache clearing
if (memoizedFunc.cache.has('outdated-key')) {
memoizedFunc.cache.delete('outdated-key');
}const safeOperation = memoize(function(input) {
if (typeof input !== 'number') {
throw new TypeError('Expected a number');
}
return input * 2;
});
try {
safeOperation('invalid'); // Throws TypeError
} catch (error) {
console.error(error.message); // "Expected a number"
}
// Errors are not cached - subsequent calls with same input will re-throw