Что такое мемоизация и чем она отличается от кеширования?
Мемоизация
Мемоизация — техника оптимизации, при которой результат выполнения чистой функции сохраняется в локальной структуре данных (обычно Map или объект) по ключу, построенному из её аргументов. При повторном вызове с теми же аргументами функция не выполняется заново, а возвращается сохранённый результат.
Ключевые свойства:
- работает только с чистыми функциями (без побочных эффектов, детерминированными);
- кеш обычно живёт в памяти процесса и привязан к конкретной функции;
- инвалидация как правило отсутствует — считается, что для одних и тех же входных данных результат никогда не меняется.
Кеширование
Кеширование — более общее понятие: сохранение любых данных (результатов вычислений, ответов API, файлов, HTML-страниц) для ускорения последующего доступа. Кеш может:
- иметь TTL (время жизни) и явную инвалидацию;
- находиться вне процесса (Redis, CDN, HTTP-кеш браузера);
- хранить данные, которые меняются во времени (курс валют, список пользователей);
- требовать стратегии вытеснения (LRU, LFU) при ограниченном размере.
Главное отличие
Мемоизация — это кеширование, но не наоборот. Мемоизация всегда про результаты функций и привязана к их аргументам как ключу; кеширование — про данные вообще, с более сложным жизненным циклом (TTL, инвалидация, распределённое хранилище).
Пример мемоизации
См. код ниже: функция memoize оборачивает дорогую чистую функцию и кеширует результат по сериализованным аргументам.
Пример кеширования
Запрос к API кешируется на 60 секунд, после чего данные считаются устаревшими и запрашиваются заново — это классическое кеширование с TTL, а не мемоизация.
Где применяется в JS/React
useMemoиuseCallbackв React — мемоизация значений и функций между рендерами по списку зависимостей;- мемоизация в алгоритмах динамического программирования (числа Фибоначчи, обход графов);
- селекторы в Redux (
reselect) — мемоизация производных данных из стора.
Подводные камни
- мемоизация нечистой функции (обращение к
Date.now(), случайным числам, внешнему состоянию) даст неверные результаты; - кеш мемоизации может расти бесконечно и вызывать утечки памяти, если не ограничивать его размер или не использовать
WeakMapдля объектных ключей; - сериализация сложных аргументов (объекты, массивы) для построения ключа кеша может быть дорогой сама по себе;
- в отличие от полноценного кеша, мемоизация обычно не учитывает время жизни данных — если исходные данные изменились, а функция формально «чистая» по сигнатуре, результат может устареть.
Что хочет услышать интервьюер
Кандидат чётко разделяет понятия: мемоизация — про результаты чистых функций, кеширование — общее понятие про любые данные
Упоминает, что мемоизация — частный случай кеширования, а не синоним
Знает про инвалидацию и TTL как признак «настоящего» кеша, отсутствующий в базовой мемоизации
Приводит примеры из практики: useMemo/useCallback, reselect, кеш HTTP-запросов
Понимает риски: утечки памяти, некорректная мемоизация нечистых функций
Пример: Простая мемоизация чистой функции
function memoize(fn) {
const cache = new Map();
return function (...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = fn(...args);
cache.set(key, result);
return result;
};
}
// Дорогая чистая функция
function fib(n) {
if (n <= 1) return n;
return fib(n - 1) + fib(n - 2);
}
const memoizedFib = memoize(fib);
memoizedFib(35); // считает честно
memoizedFib(35); // возвращает из кеша мгновенно
Пример: Кеширование с TTL (не мемоизация)
const cache = new Map();
const TTL_MS = 60_000;
async function getUserWithCache(id) {
const cached = cache.get(id);
// Данные считаются устаревшими через TTL — этого нет в чистой мемоизации
if (cached && Date.now() - cached.timestamp < TTL_MS) {
return cached.data;
}
const data = await fetch(`/api/users/${id}`).then((r) => r.json());
cache.set(id, { data, timestamp: Date.now() });
return data;
}
Типичные ошибки
Используют термины как полные синонимы, не видя разницы в объёме понятий
Пытаются мемоизировать функции с побочными эффектами или недетерминированным поведением
Не учитывают рост памяти при неограниченном кеше мемоизации
Забывают, что ключ кеша должен учитывать все аргументы, а не только часть
Путают мемоизацию на уровне рендера (useMemo) с постоянным кешированием между сессиями


