JS 利用高階函式實現函式快取(備忘模式)

SHERlocked93發表於2017-12-20

1. 高階函式

高階函式就是那種輸入引數裡面有一個或者多個函式,輸出也是函式的函式,這個在js裡面主要是利用閉包實現的,最簡單的就是經常看到的在一個函式內部輸出另一個函式,比如

var test = function() {
    return function() {}
}

這個主要是利用閉包來保持著作用域:

var add = function() {
    var num = 0;
    return function(a) {
        return num = num + a;
    }
}
add()(1);      // 1
add()(2);      // 2

這裡的兩個add()(1)和add()(2)不會互相影響,可以理解為每次執行add函式後返回的都是不同的匿名函式,就是每次add執行後return的function其實都是不同的,所以執行結果也是不會影響的。

如果換一種寫法,比如:

var add = function() {
    var num = 0;
    return function(a) {
        return num = num + a;
    }
}
var adder = add();
adder(1); // 1
adder(2); // 3

這樣的話就會在之前運算結果基礎上繼續運算,意思就是這兩個 adder 執行的時候都是呼叫的同一個 num

2. 高階函式實現快取(備忘模式)

比如有個函式:

var add = function(a) {
    return a + 1;
}

每次執行add(1)的時候都會輸出2,但是輸入1每次還是會計算一下1+1,如果是開銷很大的操作的話就比較消耗效能了,這裡其實可以對這個計算進行一次快取。
所以這裡可以利用高階函式的思想來實現一個簡單的快取,我可以在函式內部用一個物件儲存輸入的引數,如果下次再輸入相同的引數,那就比較一下物件的屬性,把值從這個物件裡面取出來。

const memorize = function(fn) {
  const cache = {}
  return function(...args) {
    const _args = JSON.stringify(args)
    return cache[_args] || (cache[_args] = fn.apply(fn, args))
  }
}
const add = function(a) {
  return a + 1
}
const adder = memorize(add)
adder(1)    // 2    cache: { '[1]': 2 }
adder(1)    // 2    cache: { '[1]': 2 }
adder(2)    // 3    cache: { '[1]': 2, '[2]': 3 }

JSON.stringify把傳給 adder 函式的引數變成了字串,並且把它當做 cache 的 key,將 add 函式執行的結果當做 value 傳到了 cache 裡面,這樣 memorize 的匿名函式執行的時候會返回cache[_args],如果cache[_args]不存在的話就返回fn.apply(fn,args),把fn.apply(fn, arguments)賦值給cache[_args]並返回。
注意:cache不可以是Map,因為Map的鍵是使用===比較的,[1]!==[1],因此即使傳入相同的物件或者陣列,那麼還是被存為不同的鍵。

const memorize = function(fn) {        //  X 錯誤示範
  const cache = new Map()
  return function(...args) {
    return cache.get(args) || cache.set(args, fn.apply(fn, args)).get(args)
  }
}
const add = function(a) {
  return a + 1
}
const adder = memorize(add)
adder(1)    // 2    cache: { [ 1 ] => 2 }
adder(1)    // 2    cache: { [ 1 ] => 2, [ 1 ] => 2 }
adder(2)    // 3    cache: { [ 1 ] => 2, [ 1 ] => 2, [ 2 ] => 3 }

本文是系列文章,可以相互參考印證,共同進步~

  1. JS 抽象工廠模式
  2. JS 工廠模式
  3. JS 建造者模式
  4. JS 原型模式
  5. JS 單例模式
  6. JS 回撥模式
  7. JS 外觀模式
  8. JS 介面卡模式
  9. JS 利用高階函式實現函式快取(備忘模式)
  10. JS 狀態模式
  11. JS 橋接模式
  12. JS 觀察者模式

網上的帖子大多深淺不一,甚至有些前後矛盾,在下的文章都是學習過程中的總結,如果發現錯誤,歡迎留言指出~

參考: <JavaScript模式>P78

相關文章