定义相等
在对函数计算进行缓存时,我们首先要先定义一下相等,一个被缓存的函数如果在以下几个方面上满足,那么就可以将其视作相等,之前返回缓存的结果即可:
- 缓存存在
- 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 falsefor (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 promisecache.set(key, result)return result} catch (err) {if (!cachePromiseRejection) {promiseCache.delete(key)}throw err}}return memoized}
cache 可以用来设置过期时间,或者是其他类型的缓存,例如数据库等。
