← Назад к вопросам
Как скрыть данные лежащие в кэше при помощи замыкания?
2.0 Middle🔥 141 комментариев
#JavaScript Core
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как скрыть данные в кэше при помощи замыкания
Замыкания (closures) - это один из самых мощных инструментов в JavaScript для создания приватных данных. Это механизм, при котором функция имеет доступ к переменным из внешней области видимости, но эти переменные остаются скрытыми от прямого доступа.
1. Основной принцип замыкания
Замыкание создается когда функция обращается к переменным из своего лексического контекста:
// Простое замыкание с приватным кэшем
function createCache() {
const cache = {}; // Приватное хранилище - скрыто от внешнего доступа
return {
// Публичный метод для получения значения
get(key) {
return cache[key];
},
// Публичный метод для установки значения
set(key, value) {
cache[key] = value;
},
// Публичный метод для проверки наличия
has(key) {
return key in cache;
},
// Публичный метод для удаления
remove(key) {
delete cache[key];
}
};
}
// Использование
const myCache = createCache();
myCache.set("user", { name: "John" });
console.log(myCache.get("user")); // { name: "John" }
// cache переменная скрыта, прямого доступа нет
console.log(myCache.cache); // undefined - не доступна!
Это основная идея: переменная cache лежит в замыкании функции и доступна только через публичные методы.
2. Практический пример с кэшированием функций
function memoize(fn) {
const cache = new Map(); // Приватный кэш
return function memoized(...args) {
const key = JSON.stringify(args); // Ключ - аргументы функции
// Проверяем кэш
if (cache.has(key)) {
console.log("Из кэша:", args);
return cache.get(key);
}
// Вычисляем результат
const result = fn.apply(this, args);
cache.set(key, result);
console.log("Вычислено:", args);
return result;
};
}
// Использование
const expensive = memoize((a, b) => {
// Долгое вычисление
return a + b;
});
console.log(expensive(2, 3)); // Вычислено: [2, 3] -> 5
console.log(expensive(2, 3)); // Из кэша: [2, 3] -> 5
// Кэш скрыт, доступа нет
console.log(expensive.cache); // undefined!
3. Кэш с TTL (Time To Live)
Кэш с автоматическим истечением:
function createCacheWithTTL(ttl = 5000) {
const cache = new Map();
return {
set(key, value) {
cache.set(key, {
value,
timestamp: Date.now()
});
},
get(key) {
if (!cache.has(key)) {
return undefined;
}
const { value, timestamp } = cache.get(key);
const elapsed = Date.now() - timestamp;
// Проверяем TTL
if (elapsed > ttl) {
cache.delete(key);
return undefined;
}
return value;
},
clear() {
cache.clear();
}
};
}
// Использование
const cache = createCacheWithTTL(3000); // TTL 3 секунды
cache.set("api-response", { data: "sensitive" });
console.log(cache.get("api-response")); // { data: "sensitive" }
setTimeout(() => {
console.log(cache.get("api-response")); // undefined - истекло
}, 4000);
4. Кэш для асинхронных операций
function createAsyncCache() {
const cache = new Map();
const pending = new Map();
return async function cached(key, asyncFn) {
// Если результат в кэше - вернуть его
if (cache.has(key)) {
return cache.get(key);
}
// Если запрос уже выполняется - дождаться его
if (pending.has(key)) {
return pending.get(key);
}
// Запустить асинхронную функцию
const promise = asyncFn()
.then(result => {
cache.set(key, result);
pending.delete(key);
return result;
})
.catch(error => {
pending.delete(key);
throw error;
});
pending.set(key, promise);
return promise;
};
}
// Использование
const apiCache = createAsyncCache();
async function fetchUser(userId) {
return apiCache(
`user-${userId}`,
() => fetch(`/api/users/${userId}`).then(r => r.json())
);
}
// Первый вызов - запрос на сервер
const user1 = await fetchUser(1);
// Второй вызов - из кэша
const user2 = await fetchUser(1);
// cache переменная скрыта, нет доступа к приватным данным
5. Защита данных от утечек
function createSecureCache(password) {
const cache = new Map();
return {
set(key, value) {
cache.set(key, value);
},
get(key) {
return cache.get(key);
},
// Безопасное очищение
clear(pwd) {
if (pwd === password) {
cache.clear();
return true;
}
console.warn("Неправильный пароль");
return false;
},
// Получить размер кэша (полезная инфо без доступа к данным)
size() {
return cache.size;
}
};
}
// Использование
const secure = createSecureCache("my-secret-pwd");
secure.set("credit-card", "1234-5678-9012-3456");
// Нельзя просто очистить
secure.clear("wrong-pwd"); // false
// С правильным паролем
secure.clear("my-secret-pwd"); // true
6. Паттерн Module (объединение замыканий)
const StorageManager = (() => {
const localData = new Map(); // Приватное хранилище
const sessionData = new Map(); // Приватное хранилище
return {
// Публичный интерфейс
saveLocal(key, value) {
localData.set(key, value);
},
getLocal(key) {
return localData.get(key);
},
saveSession(key, value) {
sessionData.set(key, value);
},
getSession(key) {
return sessionData.get(key);
},
getStats() {
return {
localSize: localData.size,
sessionSize: sessionData.size
};
}
};
})();
// Использование
StorageManager.saveLocal("user", { name: "John" });
StorageManager.saveSession("token", "abc123");
console.log(StorageManager.getStats()); // { localSize: 1, sessionSize: 1 }
// Прямого доступа к localData и sessionData нет
console.log(StorageManager.localData); // undefined
7. Кэш с инвалидацией
function createInvalidatableCache() {
const cache = new Map();
const dependents = new Map(); // Отслеживание зависимостей
return {
set(key, value, dependencies = []) {
cache.set(key, value);
// Регистрируем зависимости
dependencies.forEach(dep => {
if (!dependents.has(dep)) {
dependents.set(dep, new Set());
}
dependents.get(dep).add(key);
});
},
get(key) {
return cache.get(key);
},
invalidate(key) {
cache.delete(key);
// Инвалидировать зависимые ключи
if (dependents.has(key)) {
dependents.get(key).forEach(dependent => {
cache.delete(dependent);
});
}
}
};
}
// Использование
const depCache = createInvalidatableCache();
depCache.set("user-1", { name: "John" });
depCache.set("user-1-posts", ["post1", "post2"], ["user-1"]);
console.log(depCache.get("user-1-posts")); // ["post1", "post2"]
// Инвалидируем юзера - автоматически инвалидируются посты
depCache.invalidate("user-1");
console.log(depCache.get("user-1-posts")); // undefined
8. Практический пример в React
// Кэш для API запросов
const createQueryCache = () => {
const cache = new Map();
return {
async fetch(url, options = {}) {
const key = `${url}:${JSON.stringify(options)}`;
if (cache.has(key)) {
return cache.get(key);
}
const response = await fetch(url, options);
const data = await response.json();
cache.set(key, data);
return data;
}
};
};
// В компоненте
const queryCache = createQueryCache();
export function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
queryCache.fetch(`/api/users/${userId}`)
.then(data => setUser(data));
}, [userId]);
if (!user) return <Skeleton />;
return <div>{user.name}</div>;
}
Преимущества использования замыканий для кэша
- Инкапсуляция - данные полностью скрыты
- Безопасность - нельзя случайно перезаписать кэш
- Контроль - весь доступ идет через методы
- Чистота - нет загрязнения глобального пространства
- Предсказуемость - все операции с кэшем явные
Замыкания - идеальный инструмент для создания приватного, безопасного кэша, который защищен от прямого доступа и модификации.