#CodeNewbie

#CodingTips

#Frontend

#Fullstack

#JavaScript

#LearnToCode

#Programming

#WebDevelopment

Memoisation patterns — caching function results

Memoisation is an optimisation technique that caches function results to avoid recomputing expensive operations. When the same inputs occur again, return the cached result instead of recalculating. It is particularly powerful for recursive functions like fibonacci, expensive computations, and React's useMemo hook. Master memoisation and your code will run dramatically faster.


 

The problem: expensive repeated calculations

Without memoisation, calling a function with the same inputs multiple times forces you to recompute the result every time — wasting CPU cycles.

// ❌ Without memoisation: recomputes every time
function fibonacci(n) {
  if (n <= 1) return n;
  return fibonacci(n - 1) + fibonacci(n - 2);
}

fibonacci(40);  → Takes ~2 seconds
fibonacci(40);  → Takes ~2 seconds again (computed from scratch!)

// ✅ With memoisation: cached result is instant
const memoFib = memoise(fibonacci);
memoFib(40);  → Takes ~2 seconds (first call)
memoFib(40);  → Instant!  ← returned from cache

 

Basic memoisation with a cache object

The simplest memoisation stores results in an object (Map) using inputs as keys. If the key exists, return the cached value. Otherwise, compute and store it.

// Simple memoisation for single-argument functions
function slowFib(n) {
  if (n <= 1) return n;
  return slowFib(n - 1) + slowFib(n - 2);
}

const cache = {};
function memoFib(n) {
  if (n in cache) {
    console.log(`Returning cached result for ${n}`);
    return cache[n];
  }
  console.log(`Computing fibonacci(${n})`);
  const result = slowFib(n);
  cache[n] = result;  ← store in cache
  return result;
}

memoFib(5);  → Computing fibonacci(5) ... 5
memoFib(5);  → Returning cached result for 5 ... 5
memoFib(6);  → Computing fibonacci(6) ... 8

 

Generic memoisation helper

A generic memoisation function works with any function, including those with multiple arguments. It serializes arguments into a cache key.

// Generic memoise function for any function
function memoise(fn) {
  const cache = new Map();

  return function(...args) {
    const key = JSON.stringify(args);  ← serialize args to key

    if (cache.has(key)) {
      return cache.get(key);
    }

    const result = fn(...args);
    cache.set(key, result);
    return result;
  };
}

// Works with any function
const add = (a, b) => a + b;
const memoAdd = memoise(add);

memoAdd(2, 3);  → 5  ← computed
memoAdd(2, 3);  → 5  ← from cache
memoAdd(3, 4);  → 7  ← new cache key

 

Real-world memoisation examples

Expensive API calls

const memoise = (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;
  };
};

const fetchUser = async (id) => {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
};

const cachedFetchUser = memoise(fetchUser);

await cachedFetchUser(1);  → ~200ms API call
await cachedFetchUser(1);  → Instant!  ← from cache

Expensive calculations (fibonacci)

// Recursive fibonacci (slow without memoisation)
function fib(n) {
  if (n <= 1) return n;
  return fib(n - 1) + fib(n - 2);  ← exponential time!
}

const memoFib = memoise(fib);

// Now it is FAST thanks to memoisation
memoFib(50);  → Instant!  ← previously took minutes

// Why? Each fib(n) is computed once, then cached
// fib(50) needs fib(49) + fib(48)
// fib(49) needs fib(48) + fib(47) — but fib(48) is cached!

 

Memoisation with cache size limits (LRU)

Unbounded caches can consume infinite memory. An LRU (Least Recently Used) cache keeps only the most recent N results, evicting old ones.

// Memoise with size limit
function memoiseWithLimit(fn, limit = 10) {
  const cache = new Map();

  return function(...args) {
    const key = JSON.stringify(args);

    if (cache.has(key)) {
      cache.delete(key);  ← move to end (most recent)
      cache.set(key, cache.get(key));
      return cache.get(key);
    }

    const result = fn(...args);
    cache.set(key, result);

    // Evict oldest if over limit
    if (cache.size > limit) {
      const firstKey = cache.keys().next().value;
      cache.delete(firstKey);
    }

    return result;
  };
}

const memoFib = memoiseWithLimit(fib, 100);
// Cache will never grow beyond 100 entries

 

Key takeaways

Memoisation caches function results to avoid recomputing expensive operations. Given the same inputs, return the cached result instead of recalculating. A generic memoisation function can wrap any function and transparently cache its results. Serialize arguments to JSON to create cache keys. Unbounded caches can consume memory, so implement LRU (Least Recently Used) eviction for production use. Memoisation is particularly powerful for recursive functions, expensive computations, and frontend frameworks like React.