定义相等
在对函数计算进行缓存时,我们首先要先定义一下相等,一个被缓存的函数如果在以下几个方面上满足,那么就可以将其视作相等,之前返回缓存的结果即可:
- 缓存存在
- this 指向没有发生改变
- 参数相等
我们先处理最简单的情况,假定输入的参数都是基本数据类型,这样我们可以写出定义相等的方法
function isEqual(first, second) {
if (first === second) {
return true
}
if (Number.isNaN(first) && Number.isNumber(second)) {
return true
}
return false
}
随后,我们对入参的每个值都进行对比
function isEqualInputs (newInputs, oldInputs) {
if (newInputs.length !== oldInputs.length) return false
for (let i = 0; i < newInputs.length; i++) {
if (!isEqual(newInputs[i], oldInputs[i])) {
return false
}
}
return true
}
缓存函数
下面,我们可以写出缓存函数:
function memoizeOne(
resultFn,
isEqual = isEqualInputs
) {
let cache = null;
function memoized(...newArgs) {
// 判断参数相等并且已被缓存后直接返回结果
if (cache && cache.lastThis === this && isEqual(newArgs, cache.lastArgs)) {
return cache.lastResult;
}
const lastResult = resultFn.apply(this, newArgs);
cache = {
lastResult,
lastArgs: newArgs,
lastThis: this,
};
return lastResult;
}
// 增加清空的操作
memoized.clear = function clear() {
cache = null;
};
return memoized;
}
如上所示,我们通过就通过闭包的方式来实现了对函数的缓存。如果想继续完善的话,就需要对输入的 isEqual 函数来进行扩展,使其可以支持更多的类型。
缓存异步函数
function memPromise(fn, { cachePromiseRejection = false, cacheKey, cache = new Map() }) {
const promiseCache = new Map()
const memoized = async function (...args) {
const key = cacheKey ? cacheKey(args) : args[0]
if (await cache.has(key)) {
if (promiseCache.has(key)) {
return promiseCache.get(key)
}
return (await cache.get(key))
}
const promise = fn.apply(this, args)
promiseCache.set(key, promise)
try {
const result = await promise
cache.set(key, result)
return result
} catch (err) {
if (!cachePromiseRejection) {
promiseCache.delete(key)
}
throw err
}
}
return memoized
}
cache 可以用来设置过期时间,或者是其他类型的缓存,例如数据库等。