> Как реализовать мемоизацию функции в JavaScript (JavaScript)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: Домклик

Стек: JavaScript

> Пример ответа

Короткий ответ

Мемоизация - это оптимизация, при которой функция кэширует результаты выполнения для уникальных аргументов. Реализуется через замыкание, хранящее Map или WeakMap. При вызове проверяется, есть ли результат для переданных аргументов: если да - возвращается из кэша, если нет - вычисляется и сохраняется. Для функций с несколькими аргументами ключом может быть строка из сериализованных аргументов.

Подробное объяснение

Мемоизация основана на принципе чистой функции: при одинаковых входных данных результат всегда одинаков. Это позволяет избежать повторных вычислений для дорогих операций (рекурсия, сложные алгоритмы, запросы к API).

Базовая реализация использует замыкание с объектом-кэшем. Ключом выступает строка, полученная через JSON.stringify(args). Для одного аргумента можно использовать сам аргумент как ключ. Важный trade-off: мемоизация увеличивает потребление памяти за счёт хранения результатов.

Для рекурсивных функций (например, вычисление чисел Фибоначчи) мемоизация даёт экспоненциальный прирост производительности - сложность падает с O(2^n) до O(n). Однако мемоизация неэффективна для функций с побочными эффектами, недетерминированных функций (random, Date.now) или при редком повторении одинаковых аргументов.

На практике

В продакшене часто используют готовые решения: _.memoize из lodash или useMemo/useCallback в React. Но для понимания важно уметь написать свою реализацию.

Ключевые моменты:

  • Для сериализации аргументов используйте JSON.stringify - он обрабатывает объекты, массивы, примитивы
  • Учитывайте, что порядок ключей объекта влияет на сериализацию
  • Для методов класса нужна осторожность: контекст this может меняться
  • Можно добавить ограничение размера кэша (LRU-кэш) для предотвращения утечек памяти

Пример кода

JAVASCRIPT
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.apply(this, args);
cache.set(key, result);
return result;
};
}
// Пример с рекурсией
const fib = memoize(function(n) {
if (n <= 1) return n;
return fib(n - 1) + fib(n - 2);
});
console.log(fib(40)); // 102334155 - мгновенно

Как отвечать на собеседовании

Начните с определения мемоизации и её цели. Объясните механизм через замыкание и кэш. Упомяните trade-off между скоростью и памятью. Приведите пример, где мемоизация даёт значительный выигрыш (Фибоначчи, факториал). Упомяните ограничения: недетерминированные функции, побочные эффекты, сериализация объектов. Если спросят про WeakMap - объясните, что он не подходит, так как ключи могут быть собраны сборщиком мусора.

Что проверяет интервьюер

  • Понимание замыканий и области видимости
  • Умение работать с Map и сериализацией
  • Знание принципов функционального программирования (чистые функции)
  • Понимание компромиссов (time vs memory)
  • Способность предвидеть edge cases (this, рекурсия, объекты как аргументы)

Типичные ошибки

  • Использование объекта {} вместо Map - ключи приводятся к строке, что ломает работу с объектами
  • Игнорирование контекста this - при мемоизации методов класса теряется привязка
  • Отсутствие сериализации для нескольких аргументов - ключом становится только первый аргумент
  • Мемоизация нечистых функций - результат может быть неверным при побочных эффектах
  • Бесконтрольный рост кэша без ограничения размера - утечка памяти в долгоживущих приложениях

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью