回到文章列表

實作 javascript cache function

TWGD / 2019-01-11

題目

假設現在有一個 function 叫做 complexCalculation,裡面跑了很複雜的數學運算所以要跑很久,例如說:

console.log(complextCalculation(10)) // 印出 123,跑了 5 秒
console.log(complextCalculation(10)) // 印出 123,跑了 5 秒

可是對這種運算來說,只要傳進去的參數一樣,結果就一樣,所以我可以把結果給快取起來,再跑一次 function 的時候就不用重新做這些複雜的運算,所以理想的情形下是這樣:

var cachedComplextFunc = cached(complextCalculation)
console.log(cachedComplextFunc(10)) // 印出 123,跑了 5 秒
console.log(cachedComplextFunc(10)) // 立刻印出 123

現在請你實作出 cached 這個 function,讓上面的程式碼可以順利運行

var cachedComplextFunc = cached(complextCalculation)
console.log(cachedComplextFunc(10)) // 印出 123,跑了 5 秒
console.log(cachedComplextFunc(10)) // 立刻印出 123

解題心得:

要做到 cache 住運算結果,要運用 closure 閉包的概念。

function cached(){} 裡面有一個 object ( cache = {} ) 用來記住之前的結果,然後 return 另一個 function 出去。 用變數 cachedComplextFunc 來接這個 return 出來的 function。 之所以可以記住變數,是因為底層 scope chain 的關係,function cached(){} 裡面的 cache 這個 object 會被 keep 住。

一、當只有一個參數的時候:(參數是數字)

const cached = (func) => {
    let cache = {};
    return (key) => {
        if(key in cache){
            return cache[key];
        } else {
            let ans = func(key);
            cache[key] = ans;
            return ans;
        }
    }
}

就是把傳進來的參數當作 cache 這個 object 的 key,運算結果當作 value 給保存起來。 之後 cachedComplextFunc 有參數傳進來,若 object 有這個參數的 key,就回傳之前運算過保留的結果。 若沒有的話,就執行運算 function,然後存進 object。

二、當有多個參數的時候:(參數都是數字)

const cached = (func) => {
    let cache = {};
    return (...key) => {
        if(key in cache){
            return cache[key];
        } else {
            let ans = func(...key);
            cache[key] = ans;
            return ans;
        }
    }
}

概念跟前面一樣,只是會碰到『要怎麼把多個參數傳進去?』的問題。 ES6 可以用 rest operator ...,傳進來的參數會轉成一個 array。 另外,存進去 object 的方式也是一樣把所有傳進來的參數當作 key。

三、當參數型態除了數字也可能是字串、陣列、物件:

const cached = (func) => {
    let cache = {};
    return (...args) => {
        let key = JSON.stringify(args);
        if(key in cache){
            console.log('cached')
            return cache[key];
        }
        let ans = func(...args);
        cache[key] = ans;
        return ans;
    }
}

這邊會遇到 object 型態的參數無法儲存為 key 的問題。 所有 object 只會顯示 [object object]。 因此需要用 JSON.stringify() 轉換成 JSON 字串儲存。

其他問題:字串化後,object key 的順序,造成判定成不同 object 的問題。(未完)

重點概念整理:

  • 應用 closure 閉包,實作 cache function。
  • 變數產生或是有特殊符號的 object key ,只能用 obj[] 存取到。
  • object key 只能是字串,若不是字串則會被『強制轉換』為字串。
  • 若要傳入多個參數,ES6 可以用 Array.from(arguments)... (rest parameter) 來把參數 ( array-like ) 轉換成 array。 P.S. Arrow function 沒有自己的 arguments 物件。
  • 若要傳入的參數型態是 object,要用 JSON.stringify() 轉換成 JSON 字串,才可以儲存。(JSON.stringify() 不能處理 function )

  • 參考資料:

  • https://medium.freecodecamp.org/understanding-memoize-in-javascript-51d07d19430e
  • https://www.sitepoint.com/implementing-memoization-in-javascript/