Skip to content

缓存函数

缓存函数的作用

缓存另一个函数,当被缓存的函数的调用入参与历史调用一样,不会触发被缓存函数的调用,而是直接返回结果

一般对于一些计算量很大,或逻辑复杂的函数来说很有用,可以提高性能

源码

ts
type Record<K extends keyof any, T> = {
  [P in K]: T;
};

/**
 * Create a cached version of a pure function.
 */
export function cached<R>(fn: (str: string) => R): (sr: string) => R {
  const cache: Record<string, R> = Object.create(null);
  return function cachedFn(str: string) {
    const hit = cache[str];
    return hit || (cache[str] = fn(str));
  };
}

使用:

ts
const genStaticKeysCached = cached(genStaticKeys);

function add(keys: string): Function {
  return makeMap(
    "type,tag,attrsList,attrsMap,plain,parent,children,attrs,start,end,rawAttrsMap" +
      (keys ? "," + keys : "")
  );
}

增强版本

观察源码可以看出,Vue 中被缓存的函数只接受一个入参 str, 且类型是固定的, 只能是字符串,这只能用于特定场景

增强版本要做的就是不限制入参的数量,类型,让缓存函数变得更通用

js
/**
 * 参数字典
 */
class DictNode {
  primiteMap = new Map();
  weakMap = new WeakMap();
  res = null;
  saved = false;

  /**
   * 判断当前参数是否为对象类型
   */
  isObject(t) {
    return typeof t === "function" || (typeof t === "object" && t !== null);
  }
  /**
   * 获取当前参数应该选用的Map类型,
   * 引用类型用WeakMap,原始类型用Map,
   * 解决Map垃圾回收问题
   * */
  getMap(v) {
    return this.isObject(v) ? this.weakMap : this.primiteMap;
  }

  /**
   * 为当前参数字典设置值
   */
  setRes(v) {
    this.res = v;
    this.saved = true;
  }
}

/* 缓存函数(记忆函数) */
function cached(fn) {
  // 先初始化参数字典,第一个参数(args[0])会首先存入
  const root = new DictNode();
  return function cachedFn(...args) {
    let map;
    let dict = root; // dict先为根字典实例,dict会随着args循环变化而变成每一个参数对应的字典实例
    // 遍历每一个参数,一层一层组装参数字典Hash表
    for (const item of args) {
      map = dict.getMap(item);
      if (!map.has(item)) map.set(item, new DictNode());
      dict = map.get(item); // args循环完后,dict表示最后一个参数的字典实例
    }
    // 循环结束就得到了最后一个参数对应的字典, 可以进行取值与填充了
    if (!dict.saved) dict.setRes(fn(...args));
    return dict.res;
  };
}