memoization 來源於拉丁語 memorandum ("to be remembered"),不要與 memorization 混淆了。javascript
首先來看一下維基百科的描述:java
In computing, memoization or memoisation is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again.
簡單來講,memoization 是一種優化技術,主要用於經過存儲昂貴的函數調用的結果來加速計算機程序,並在再次發生相同的輸入時返回緩存的結果。git
本文首先介紹一個簡單的使用 memoization 優化技術的例子,而後解讀 underscore 和 reselect 庫中使用 memoization 的源碼,加深理解。github
不假思索,咱們會當即寫下以下的代碼:redux
const factorial = n => { if (n === 1) { return 1 } else { return factorial(n - 1) * n } };
const cache = [] const factorial = n => { if (n === 1) { return 1 } else if (cache[n - 1]) { return cache[n - 1] } else { let result = factorial(n - 1) * n cache[n - 1] = result return result } };
常見的方式是 閉包 和 memoization 一塊兒搭配使用:緩存
const factorialMemo = () => { const cache = [] const factorial = n => { if (n === 1) { return 1 } else if (cache[n - 1]) { console.log(`get factorial(${n}) from cache...`) return cache[n - 1] } else { let result = factorial(n - 1) * n cache[n - 1] = result return result } } return factorial }; const factorial = factorialMemo();
繼續變形,下面這種編寫方式是最多見的形式。閉包
const factorialMemo = func => { const cache = [] return function(n) { if (cache[n - 1]) { console.log(`get factorial(${n}) from cache...`) return cache[n - 1] } else { const result = func.apply(null, arguments) cache[n - 1] = result return result } } } const factorial = factorialMemo(function(n) { return n === 1 ? 1 : factorial(n - 1) * n });
從階乘的這個例子能夠知道 memoization 是一個空間換時間的方式,存儲執行結果,下次再次發生相同的輸入會直接輸出結果,提升了執行的速度。app
// Memoize an expensive function by storing its results. _.memoize = function(func, hasher) { var memoize = function(key) { var cache = memoize.cache; var address = '' + (hasher ? hasher.apply(this, arguments) : key); if (!_.has(cache, address)) cache[address] = func.apply(this, arguments); return cache[address]; }; memoize.cache = {}; return memoize; };
代碼一目瞭然,使用 _.memoize 來實現階乘以下:函數
const factorial = _.memoize(function(n) { return n === 1 ? 1 : factorial(n - 1) * n });
參照這個源碼,上面的階乘繼續能夠變形以下:優化
const factorialMemo = func => { const memoize = function(n) { const cache = memoize.cache if (cache[n - 1]) { console.log(`get factorial(${n}) from cache...`) return cache[n - 1] } else { const result = func.apply(null, arguments) cache[n - 1] = result return result } } memoize.cache = [] return memoize } const factorial = factorialMemo(function(n) { return n === 1 ? 1 : factorial(n - 1) * n });
export function defaultMemoize(func, equalityCheck = defaultEqualityCheck) { let lastArgs = null let lastResult = null // we reference arguments instead of spreading them for performance reasons return function () { if (!areArgumentsShallowlyEqual(equalityCheck, lastArgs, arguments)) { // apply arguments instead of spreading for performance. lastResult = func.apply(null, arguments) } lastArgs = arguments return lastResult } };
從源碼能夠知道當 lastArgs 與 arguments 相同的時候,就不會再執行 func。
memoization 是一種優化技術,避免一些沒必要要的重複計算,能夠提升計算速度。