定义相等

在对函数计算进行缓存时,我们首先要先定义一下相等,一个被缓存的函数如果在以下几个方面上满足,那么就可以将其视作相等,之前返回缓存的结果即可:

  • 缓存存在
  • this 指向没有发生改变
  • 参数相等

我们先处理最简单的情况,假定输入的参数都是基本数据类型,这样我们可以写出定义相等的方法

  1. function isEqual(first, second) {
  2. if (first === second) {
  3. return true
  4. }
  5. if (Number.isNaN(first) && Number.isNumber(second)) {
  6. return true
  7. }
  8. return false
  9. }

随后,我们对入参的每个值都进行对比

  1. function isEqualInputs (newInputs, oldInputs) {
  2. if (newInputs.length !== oldInputs.length) return false
  3. for (let i = 0; i < newInputs.length; i++) {
  4. if (!isEqual(newInputs[i], oldInputs[i])) {
  5. return false
  6. }
  7. }
  8. return true
  9. }

这样,我们就完成了基本的输入参数的对比。

缓存函数

下面,我们可以写出缓存函数:

  1. function memoizeOne(
  2. resultFn,
  3. isEqual = isEqualInputs
  4. ) {
  5. let cache = null;
  6. function memoized(...newArgs) {
  7. // 判断参数相等并且已被缓存后直接返回结果
  8. if (cache && cache.lastThis === this && isEqual(newArgs, cache.lastArgs)) {
  9. return cache.lastResult;
  10. }
  11. const lastResult = resultFn.apply(this, newArgs);
  12. cache = {
  13. lastResult,
  14. lastArgs: newArgs,
  15. lastThis: this,
  16. };
  17. return lastResult;
  18. }
  19. // 增加清空的操作
  20. memoized.clear = function clear() {
  21. cache = null;
  22. };
  23. return memoized;
  24. }

如上所示,我们通过就通过闭包的方式来实现了对函数的缓存。如果想继续完善的话,就需要对输入的 isEqual 函数来进行扩展,使其可以支持更多的类型。

缓存异步函数

  1. function memPromise(fn, { cachePromiseRejection = false, cacheKey, cache = new Map() }) {
  2. const promiseCache = new Map()
  3. const memoized = async function (...args) {
  4. const key = cacheKey ? cacheKey(args) : args[0]
  5. if (await cache.has(key)) {
  6. if (promiseCache.has(key)) {
  7. return promiseCache.get(key)
  8. }
  9. return (await cache.get(key))
  10. }
  11. const promise = fn.apply(this, args)
  12. promiseCache.set(key, promise)
  13. try {
  14. const result = await promise
  15. cache.set(key, result)
  16. return result
  17. } catch (err) {
  18. if (!cachePromiseRejection) {
  19. promiseCache.delete(key)
  20. }
  21. throw err
  22. }
  23. }
  24. return memoized
  25. }

cache 可以用来设置过期时间,或者是其他类型的缓存,例如数据库等。