Мемоізація - кешування результату функції за її аргументами: повторний виклик з тими самими аргументами повертає збережений результат замість повторного обчислення.
function memoize(fn) {
const cache = new Map();
return (arg) => {
if (!cache.has(arg)) {
cache.set(arg, fn(arg));
}
return cache.get(arg);
};
}
const slowSquare = (n) => { /* важке обчислення */ return n * n; };
const fastSquare = memoize(slowSquare);
Кеш живе в замиканні - прямо до нього ніхто не дістанеться.
Умови, за яких мемоізація коректна:
- функція чиста: результат залежить лише від аргументів і не має побічних ефектів. Мемоізувати
getCurrentUser()чи функцію зDate.now()всередині - отримати застарілі дані; - аргументи можна порівняти як ключ. Для примітивів - просто. Для об'єктів
Mapпорівнює за посиланням: два однакові за вмістом об'єкти - два різні ключі. Для кількох аргументів ключ доводиться будувати (JSON.stringify(args)), а це має свою ціну.
Коли мемоізація шкодить:
- дешеві функції. Перевірка кешу, побудова ключа й зберігання можуть коштувати більше, ніж саме обчислення;
- рідкісні повтори. Якщо аргументи майже завжди нові, кеш лише росте;
- пам'ять. Кеш без обмеження - витік. Для довгоживучих сторінок потрібне обмеження розміру (LRU) або
WeakMap, якщо ключ - об'єкт, і запис має зникати разом з ним; - змінні аргументи. Якщо об'єкт-аргумент змінили «на місці», посилання те саме - кеш поверне результат для старого вмісту.
Мемоізація у фреймворках:
- Vue
computed- мемоізоване значення, що перераховується лише при зміні реактивних залежностей; - React
useMemo,useCallback,memo- зберігають значення чи функцію між рендерами. Найчастіша помилка - обгортати ними все підряд: порівняння залежностей теж коштує, а код стає складнішим. Їх застосовують при виміряній проблемі продуктивності чи для стабільних посилань, від яких залежать інші ефекти; - у Laravel аналог -
once(), що мемоізує результат у межах запиту.
Класичний приклад виправданої мемоізації - рекурсія з перекриттям підзадач (числа Фібоначчі, динамічне програмування): без кешу експоненційна складність, з кешем - лінійна.