Як реалізувати мемоізацію функції?
1. Базова реалізація (для однієї функції з примітивними аргументами)
function memoize(fn) {
const cache = {};
return function (...args) {
const key = JSON.stringify(args); // створюємо ключ з аргументів
if (key in cache) {
console.log('Беремо з кешу:', key);
return cache[key];
}
console.log('Обчислюємо:', key);
const result = fn(...args);
cache[key] = result;
return result;
};
}Приклад використання:
function slowAdd(a, b) {
// імітація важких обчислень
for (let i = 0; i < 1e8; i++);
return a + b;
}
const memoAdd = memoize(slowAdd);
console.log(memoAdd(2, 3)); // Обчислюємо
console.log(memoAdd(2, 3)); // Беремо з кешу
console.log(memoAdd(4, 5)); // ОбчислюємоТепер повторні виклики з тими самими аргументами не перераховуються - просто беруться з пам'яті.
2. Універсальна реалізація (з Map замість об'єкта)
Краще використовувати
Map, тому що він швидший і надійніший для ключів будь-якої структури.
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(...args);
cache.set(key, result);
return result;
};
}Перевага: можна безпечно кешувати значення навіть для складних аргументів (arrays, objects).
3. Просунута версія з обмеженням розміру кешу (LRU Cache)
Щоб кеш не зростав нескінченно і не "з'їдав" пам'ять.
function memoize(fn, limit = 5) {
const cache = new Map();
return function (...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
// оновлюємо порядок використання
const value = cache.get(key);
cache.delete(key);
cache.set(key, value);
return value;
}
const result = fn(...args);
cache.set(key, result);
// якщо кеш занадто великий - видаляємо найстаріший елемент
if (cache.size > limit) {
const oldestKey = cache.keys().next().value;
cache.delete(oldestKey);
}
return result;
};
}Тепер кеш "ковзний": зберігає останні N викликів, що корисно для реальних застосунків.
4. Приклад реального застосування - мемоізація рекурсивної функції (фібоначчі)
function memoize(fn) {
const cache = {};
return function (n) {
if (n in cache) return cache[n];
const result = fn(n);
cache[n] = result;
return result;
};
}
const fib = memoize(function f(n) {
if (n <= 1) return n;
return f(n - 1) + f(n - 2);
});
console.log(fib(40)); // Швидко, попри рекурсіюБез мемоізації це було б десятки мільйонів викликів, а з кешем - лише 40.
5. Реалізація з підтримкою асинхронних функцій
Коли потрібно кешувати результати
fetch,axios,db.queryтощо.
function memoizeAsync(fn) {
const cache = new Map();
return async function (...args) {
const key = JSON.stringify(args);
if (cache.has(key)) return cache.get(key);
const promise = fn(...args).then(result => {
cache.set(key, result);
return result;
});
cache.set(key, promise);
return promise;
};
}Приклад:
const fetchUser = memoizeAsync(async (id) => {
const res = await fetch(`https://jsonplaceholder.typicode.com/users/${id}`);
return res.json();
});
await fetchUser(1); // перший раз - HTTP-запит
await fetchUser(1); // другий раз - миттєво, з кешу6. Коли і де використовувати мемоізацію
| Сценарій | Підходить? | Чому |
|---|---|---|
| Дорогі обчислення | Так | Пришвидшує повторні виклики |
| Повторювані аргументи | Так | Вигода від кешу |
| Різні аргументи щоразу | Ні | Кеш марний |
| HTTP-запити, БД | Обережно | Можна, якщо дані рідко змінюються |
| Великі дані | Обережно | Слідкуй за пам'яттю |
Резюме
Мемоізація - це кешування результатів функції, щоб пришвидшити повторні виклики. У JS реалізується через замикання, сховище (
Map/Object) і серіалізацію аргументів (JSON.stringify).
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.