Какие структуры данных использовать для хранения параметров и результатов мемоизации в JavaScript
Что проверяют
Понимаете ли вы, как ключ кэша связан с аргументами функции и чем за это расплачивается каждая структура. Следом обычно просят написать memoize для функции с несколькими аргументами и спрашивают, как не допустить утечки памяти.
Как отвечать
- обычный объект: ключи приводятся к строке, поэтому
1и"1"совпадают, а любой объект превращается в"[object Object]". У{}есть прототип, иmemo["constructor"]вернёт функцию, которую никто не кэшировал. Если объект, тоObject.create(null)и проверка черезin; Map: ключи любого типа, сравнение по SameValueZero, объекты по ссылке. Естьsizeи порядок вставки, поэтому простой LRU пишется в пару строк: при попадании удалить и вставить заново, при переполнении удалитьmap.keys().next().value;WeakMap: ключи только объекты, запись не мешает сборщику мусора. Подходит для кэша вычислений над конкретным объектом: объект исчез, запись исчезла сама. Перебрать записи и узнать размер нельзя;- несколько аргументов: либо строковый ключ через
JSON.stringify(args), либо дерево вложенныхMapиWeakMap, по уровню на аргумент. Второй вариант корректен для объектов и не тратит время на сериализацию.
Свяжите с практикой фронтенда. useMemo хранит только последнее значение и сравнивает зависимости через Object.is. Reselect 5 по умолчанию мемоизирует селекторы через weakMapMemoize, а cache в React для серверных компонентов тоже устроен как дерево из Map и WeakMap.
Где спотыкаются
Проверяют попадание через if (memo[arg]). Для результатов 0, "" и false кэш не срабатывает никогда, функция считает заново. Нужны has или in.
Строят ключ через JSON.stringify для объектов. {a:1,b:2} и {b:2,a:1} дают разные ключи, а функции и undefined теряются.
Заводят бесконечный кэш на Map в модуле. На долгоживущей вкладке или на сервере он растёт без предела, нужны ограничение размера, TTL или WeakMap.