主题
缓存函数
缓存函数的作用
缓存另一个函数,当被缓存的函数的调用入参与历史调用一样,不会触发被缓存函数的调用,而是直接返回结果
一般对于一些计算量很大,或逻辑复杂的函数来说很有用,可以提高性能
源码
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;
};
}