使用记忆器进行繁重的计算功能

如果要在处理器(客户端或服务器端)上构建可能很重的函数,则可能需要考虑一个 memoizer ,它是先前函数执行缓存及其返回值。这允许你检查之前是否传递了函数的参数。请记住,纯函数是那些给出输入,返回相应的唯一输出并且不会在其范围之外引起副作用的函数,所以,不应该将 memoizers 添加到不可预测的函数或依赖于外部资源的函数(如 AJAX 调用或随机返回值)。

假设我有一个递归因子函数:

function fact(num) {
  return (num === 0)? 1 : num * fact(num - 1);
}

例如,如果我将小值从 1 传递到 100,那么就没有问题,但是一旦我们开始深入,我们可能会炸毁调用堆栈或者让我们正在执行此操作的 Javascript 引擎有点痛苦,特别是如果引擎不计算尾部调用优化(尽管 Douglas Crockford 说本机 ES6 包含尾部调用优化)。

我们可以硬编码我们自己的字典从 1 到上帝知道什么数字与他们相应的阶乘,但是,我不确定我是否建议! 让我们创建一个 memoizer,好吗?

var fact = (function() {
  var cache = {}; // Initialise a memory cache object
  
  // Use and return this function to check if val is cached
  function checkCache(val) {
    if (val in cache) {
      console.log('It was in the cache :D');
      return cache[val]; // return cached
    } else {
      cache[val] = factorial(val); // we cache it
      return cache[val]; // and then return it
    }
    
    /* Other alternatives for checking are:
    || cache.hasOwnProperty(val) or !!cache[val]
    || but wouldn't work if the results of those
    || executions were falsy values.
    */
  }

  // We create and name the actual function to be used
  function factorial(num) {
    return (num === 0)? 1 : num * factorial(num - 1);
  } // End of factorial function

  /* We return the function that checks, not the one
  || that computes because  it happens to be recursive,
  || if it weren't you could avoid creating an extra
  || function in this self-invoking closure function.
  */
  return checkCache; 
}());

现在我们可以开始使用它了:

StackOverflow 文档

现在我开始思考我做了什么,如果我从 1 递增而不是从 num 递减,我可以递归地缓存缓存中从 1 到 num 的所有因子,但我会留给你。

这很好但是如果我们有多个参数怎么办?这是个问题?不完全是,我们可以做一些很好的技巧,比如在 arguments 数组上使用 JSON.stringify(),或者甚至是函数依赖的值列表(对于面向对象的方法)。这样做是为了生成一个包含所有参数和依赖项的唯一键。

我们还可以创建一个 memoizes 其他函数的函数,使用与之前相同的作用域概念(返回使用原始函数且可以访问缓存对象的新函数):

警告:ES6 语法,如果你不喜欢它,替换…什么都没有,并使用 var args = Array.prototype.slice.call(null, arguments); 技巧; 用 var 替换 const 和 let,以及你已经知道的其他东西。

function memoize(func) {
  let cache = {};

  // You can opt for not naming the function
  function memoized(...args) {
    const argsKey = JSON.stringify(args);
    
    // The same alternatives apply for this example
    if (argsKey in cache) {
      console.log(argsKey + ' was/were in cache :D');
      return cache[argsKey];
    } else {
      cache[argsKey] = func.apply(null, args); // Cache it
      return cache[argsKey]; // And then return it
    }
  }

  return memoized; // Return the memoized function
}

现在请注意,这将适用于多个参数但在面向对象的方法中没有多大用处,我认为,你可能需要一个额外的依赖对象。此外,func.apply(null, args) 可以替换为 func(...args),因为数组解构将分别发送它们而不是作为数组形式。另外,仅供参考,将数组作为参数传递给 func 将不起作用,除非你像我一样使用 Function.prototype.apply

要使用上述方法,你只需:

const newFunction = memoize(oldFunction);

// Assuming new oldFunction just sums/concatenates:
newFunction('meaning of life', 42);
// -> "meaning of life42"

newFunction('meaning of life', 42); // again
// => ["meaning of life",42] was/were in cache :D
// -> "meaning of life42"