How to create a memoization function in JavaScript
Memoization caches function results based on input arguments, dramatically improving performance for expensive computations. As the creator of CoreUI with 25 years of JavaScript optimization experience, I’ve used memoization to reduce calculation times from seconds to milliseconds in production applications.
The most effective approach creates a higher-order function that wraps the target function with a caching layer.
Direct Answer
Create a memoization wrapper that caches results by argument signature.
const memoize = (fn) => {
const cache = new Map()
return (...args) => {
const key = JSON.stringify(args)
if (cache.has(key)) {
return cache.get(key)
}
const result = fn(...args)
cache.set(key, result)
return result
}
}
Usage Example
const fibonacci = memoize((n) => {
if (n <= 1) return n
return fibonacci(n - 1) + fibonacci(n - 2)
})
console.log(fibonacci(40)) // Fast: 102334155
console.log(fibonacci(40)) // Instant: cached result
Advanced Memoization
With custom cache key and size limit:
const memoize = (fn, options = {}) => {
const {
maxSize = 100,
keyGenerator = (...args) => JSON.stringify(args)
} = options
const cache = new Map()
return (...args) => {
const key = keyGenerator(...args)
if (cache.has(key)) {
return cache.get(key)
}
const result = fn(...args)
if (cache.size >= maxSize) {
const firstKey = cache.keys().next().value
cache.delete(firstKey)
}
cache.set(key, result)
return result
}
}
const expensiveCalculation = memoize(
(a, b) => {
console.log('Computing...')
return a * b + Math.random()
},
{
maxSize: 50,
keyGenerator: (a, b) => `${a}-${b}`
}
)
Memoize Object Methods
const memoizeMethod = (target, propertyKey, descriptor) => {
const originalMethod = descriptor.value
const cache = new Map()
descriptor.value = function(...args) {
const key = JSON.stringify(args)
if (cache.has(key)) {
return cache.get(key)
}
const result = originalMethod.apply(this, args)
cache.set(key, result)
return result
}
return descriptor
}
class Calculator {
@memoizeMethod
factorial(n) {
if (n <= 1) return 1
return n * this.factorial(n - 1)
}
}
WeakMap for Object Arguments
For objects as arguments, use WeakMap to prevent memory leaks:
const memoize = (fn) => {
const cache = new WeakMap()
return (obj) => {
if (cache.has(obj)) {
return cache.get(obj)
}
const result = fn(obj)
cache.set(obj, result)
return result
}
}
const processUser = memoize((user) => {
return {
fullName: `${user.firstName} ${user.lastName}`,
age: new Date().getFullYear() - user.birthYear
}
})
Clear Cache
Add cache management:
const memoize = (fn) => {
const cache = new Map()
const memoized = (...args) => {
const key = JSON.stringify(args)
if (cache.has(key)) {
return cache.get(key)
}
const result = fn(...args)
cache.set(key, result)
return result
}
memoized.clear = () => cache.clear()
memoized.delete = (...args) => cache.delete(JSON.stringify(args))
memoized.size = () => cache.size
return memoized
}
const calculate = memoize((n) => n * 2)
console.log(calculate.size()) // 0
calculate(5)
console.log(calculate.size()) // 1
calculate.clear()
Best Practice Note
This is the same memoization pattern we use in CoreUI components for expensive rendering calculations. Memoization is most effective for pure functions with repeated calls and limited argument variations. For React components, use React.memo() instead.
Related Articles
For related performance optimizations, check out how to debounce a function in JavaScript and how to optimize loop performance in JavaScript.



