缓存代理
使用代理模式缓存函数计算结果,避免重复计算
问题
实现缓存代理,对函数的计算结果进行缓存,相同参数直接返回缓存值。
解答
基本缓存代理
// 计算乘积的函数(模拟耗时操作)
function multiply(...args) {
console.log('开始计算...');
return args.reduce((a, b) => a * b, 1);
}
// 缓存代理
const proxyMultiply = (function() {
const cache = {};
return function(...args) {
const key = args.join(',');
if (key in cache) {
console.log('从缓存获取');
return cache[key];
}
const result = multiply(...args);
cache[key] = result;
return result;
};
})();
// 测试
console.log(proxyMultiply(1, 2, 3, 4)); // 开始计算... 24
console.log(proxyMultiply(1, 2, 3, 4)); // 从缓存获取 24
通用缓存代理工厂
// 创建缓存代理的工厂函数
function createCacheProxy(fn, keyGenerator) {
const cache = new Map();
// 默认的 key 生成器
const getKey = keyGenerator || ((...args) => JSON.stringify(args));
return function(...args) {
const key = getKey(...args);
if (cache.has(key)) {
return cache.get(key);
}
const result = fn.apply(this, args);
cache.set(key, result);
return result;
};
}
// 使用示例
function fibonacci(n) {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
// 带缓存的斐波那契
const cachedFib = createCacheProxy(function fib(n) {
if (n <= 1) return n;
return cachedFib(n - 1) + cachedFib(n - 2);
});
console.log(cachedFib(40)); // 快速返回 102334155
带过期时间的缓存代理
function createExpirableCache(fn, ttl = 60000) {
const cache = new Map();
return function(...args) {
const key = JSON.stringify(args);
const cached = cache.get(key);
// 检查缓存是否存在且未过期
if (cached && Date.now() - cached.timestamp < ttl) {
return cached.value;
}
const result = fn.apply(this, args);
cache.set(key, {
value: result,
timestamp: Date.now()
});
return result;
};
}
// 使用示例:缓存 5 秒
const cachedFetch = createExpirableCache(async (url) => {
const res = await fetch(url);
return res.json();
}, 5000);
使用 Proxy 实现
function withCache(fn) {
const cache = new Map();
return new Proxy(fn, {
apply(target, thisArg, args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = Reflect.apply(target, thisArg, args);
cache.set(key, result);
return result;
}
});
}
// 使用
const add = withCache((a, b) => {
console.log('计算中');
return a + b;
});
add(1, 2); // 计算中 -> 3
add(1, 2); // 3(从缓存)
关键点
- 缓存代理在原函数外包一层,拦截调用并缓存结果
- 缓存 key 的生成要能唯一标识参数组合,常用
JSON.stringify - 使用
Map比普通对象更适合做缓存容器 - 实际场景需考虑缓存过期、缓存大小限制(LRU)
- 适用于纯函数、计算密集型操作、重复 API 请求等场景
目录