缓存代理

使用代理模式缓存函数计算结果,避免重复计算

问题

实现缓存代理,对函数的计算结果进行缓存,相同参数直接返回缓存值。

解答

基本缓存代理

// 计算乘积的函数(模拟耗时操作)
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 请求等场景