> Как реализовать функцию memo для кеширования результатов вызовов по аргументам в JavaScript (JavaScript)
Уровень: senior · Роль: frontend · Категория: Технические вопросы
Компании: 1221 Systems
Стек: JavaScript
> Пример ответа
Короткий ответ
Функция memo кеширует результаты вызовов на основе аргументов. Реализация включает хранение результатов в Map или WeakMap, где ключом выступает сериализованная строка аргументов или сам первый аргумент (для объектов). Важно учитывать ограничения: сериализация через JSON.stringify не работает с функциями, undefined, Symbol и циклическими ссылками. Для простых случаев используют строковое представление, для сложных - кастомные хеш-функции или WeakMap для объектных ключей.
Подробное объяснение
Memoization - это оптимизация, при которой результат вызова функции сохраняется и возвращается при повторном вызове с теми же аргументами. Основная цель - избежать повторных дорогостоящих вычислений.
Ключевые аспекты реализации:
- Хранилище: обычно используется
Mapили объект. Для объектных аргументов, которые могут быть собраны сборщиком мусора, предпочтительнееWeakMap. - Ключ: сериализация аргументов в строку. Самый простой способ -
JSON.stringify(arguments), но он не покрывает все типы (undefined, функции, Symbol). Альтернатива - кастомная сериализация или использование первого аргумента как ключа (если он уникальный объект). - Контекст: для методов объекта нужно учитывать
this, иначе кеш будет общим для всех вызовов. - Ограничения: memoization не подходит для функций с побочными эффектами, недетерминированных функций (зависящих от времени, random) или функций с большим количеством уникальных аргументов (кеш будет расти бесконечно).
На практике
В реальных проектах memoization применяется для:
- Вычислительно тяжелых чистых функций (например, сортировка, фильтрация больших массивов)
- Результатов API-запросов (с осторожностью, так как данные могут устареть)
- Рекурсивных алгоритмов (например, числа Фибоначчи, факториал)
Популярные библиотеки: lodash.memoize, reselect для Redux. В React - useMemo и memo для компонентов.
Важно: не мемоизировать функции, которые вызываются редко или с уникальными аргументами - overhead от кеширования может превысить выгоду.
Пример кода
JAVASCRIPTfunction memo(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;};}// Пример с WeakMap для объектных аргументовfunction memoWeak(fn) {const cache = new WeakMap();return function(obj) {if (cache.has(obj)) {return cache.get(obj);}const result = fn.call(this, obj);cache.set(obj, result);return result;};}
Как отвечать на собеседовании
Начни с краткого определения memoization и его цели. Затем опиши базовую реализацию через Map и сериализацию аргументов. Обязательно упомяни ограничения: проблемы с объектами, циклическими ссылками, недетерминированными функциями. Предложи улучшения: WeakMap для объектов, кастомные хеш-функции, ограничение размера кеша (LRU cache). Покажи понимание trade-off между памятью и производительностью. Если спросят про контекст - объясни, что для методов нужно передавать this через apply или call.
Что проверяет интервьюер
- Понимание концепции чистых функций и побочных эффектов
- Умение работать с Map, WeakMap, сериализацией
- Знание ограничений JSON.stringify
- Понимание компромиссов между памятью и скоростью
- Способность предложить оптимизации (LRU, ограничение размера кеша)
- Умение учитывать контекст вызова (this)
Типичные ошибки
- Использование объекта
{}вместо Map (ключи только строки) - Игнорирование
thisпри мемоизации методов - Сериализация через
args.toString()илиargs.join(',')- не различает разные типы - Отсутствие обработки циклических ссылок в аргументах
- Мемоизация функций с побочными эффектами или недетерминированных
- Бесконечный рост кеша без ограничений (утечка памяти)
- Использование
JSON.stringifyдля аргументов, содержащих функции или undefined
> Похожие задачи по frontend
Как реализовать бесконечный скролл
Как избежать конкатенации чисел как строк в функции суммы аргументов в JavaScript?
В чем разница между document.getElementsByClassName и querySelectorAll в JavaScript
Как парсится и обрабатывается JavaScript в браузере
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью