> Как сравнить массивы аргументов для кеширования в функции memo в JavaScript (JavaScript)

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

Компании: IT-One, 1221 Systems, Яндекс

Стек: JavaScript

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

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

Для сравнения массивов аргументов в memo-функции нужно использовать глубокое сравнение, так как ссылочное сравнение (===) не работает для массивов. Основные подходы: JSON.stringify (простой, но с ограничениями), рекурсивное сравнение элементов, или библиотеки вроде lodash.isEqual. Выбор зависит от требований к производительности и типов данных в аргументах.

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

Кеширование результатов функций (memoization) требует сравнения предыдущих и текущих аргументов, чтобы определить, нужно ли пересчитывать результат. Для примитивных типов достаточно строгого сравнения (===), но массивы - это объекты, и сравнение по ссылке не подходит: два массива с одинаковыми элементами будут считаться разными.

Основные стратегии сравнения массивов аргументов:

  1. Сериализация через JSON.stringify - преобразует массив в строку, которая используется как ключ кеша. Просто, но не работает с функциями, undefined, Symbol, циклическими ссылками, и порядок ключей в объектах может влиять на результат.

  2. Рекурсивное поэлементное сравнение - обходит каждый элемент массива и сравнивает их рекурсивно. Более точное, но может быть медленным для глубоко вложенных структур.

  3. Использование библиотек - lodash.isEqual или fast-deep-equal предоставляют оптимизированное глубокое сравнение.

  4. Хэширование аргументов - создание уникального хэша (например, через hash-sum или собственный алгоритм) для быстрого поиска в Map.

Trade-off: JSON.stringify быстрее для простых данных, но может давать ложные совпадения при разных порядках ключей объекта. Рекурсивное сравнение точнее, но медленнее для больших массивов.

На практике

В реальных проектах для memo-функций часто используют комбинацию подходов. Для простых случаев с примитивными аргументами достаточно JSON.stringify. Если аргументы включают объекты или функции, лучше использовать библиотеку вроде lodash.isEqual или написать кастомное сравнение с учетом специфики данных.

Важно учитывать, что memoization с глубоким сравнением аргументов может быть дороже, чем сам вычисляемый результат. Поэтому такой подход оправдан только для тяжелых вычислений.

Пример кода

JAVASCRIPT
// Простая memo-функция с JSON.stringify
function 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;
};
}
// Более надежная версия с глубоким сравнением
function memoDeep(fn) {
const cache = [];
return function(...args) {
const entry = cache.find(([cachedArgs]) =>
cachedArgs.length === args.length &&
cachedArgs.every((arg, i) => deepEqual(arg, args[i]))
);
if (entry) return entry[1];
const result = fn.apply(this, args);
cache.push([args, result]);
return result;
};
}
// Вспомогательная функция глубокого сравнения
function deepEqual(a, b) {
if (a === b) return true;
if (typeof a !== typeof b) return false;
if (Array.isArray(a) && Array.isArray(b)) {
return a.length === b.length && a.every((v, i) => deepEqual(v, b[i]));
}
if (a && typeof a === 'object' && b && typeof b === 'object') {
const keysA = Object.keys(a);
const keysB = Object.keys(b);
return keysA.length === keysB.length && keysA.every(k => deepEqual(a[k], b[k]));
}
return false;
}

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

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

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

Интервьюер оценивает понимание ссылочных типов в JavaScript, знание механизмов сравнения, умение выбирать подходящий инструмент под задачу. Также проверяется способность рассуждать о производительности и edge cases (циклические ссылки, функции, undefined). Важно показать, что ты не просто знаешь JSON.stringify, а понимаешь его ограничения.

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

  • Использование только JSON.stringify без учета его ограничений (не работает с undefined, функциями, Symbol).
  • Сравнение массивов через arr1 === arr2, что всегда false для разных массивов с одинаковым содержимым.
  • Игнорирование производительности: глубокое сравнение на каждый вызов может быть дороже самого вычисления.
  • Неучет контекста (this) при создании memo-функции.
  • Хранение всех результатов без ограничения размера кеша, что приводит к утечке памяти.

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

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