使用記憶器進行繁重的計算功能

如果要在處理器(客戶端或伺服器端)上構建可能很重的函式,則可能需要考慮一個 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"