JavaScript 函數(shù)記憶

本文已整理到 Github,地址 ?? blog。

如果我的內(nèi)容幫助到了您,歡迎點(diǎn)個(gè) Star ?????? 鼓勵(lì)鼓勵(lì) :) ~~

我希望我的內(nèi)容可以幫助你?,F(xiàn)在我專注于前端領(lǐng)域,但我也將分享我在有限的時(shí)間內(nèi)看到和感受到的東西。


Memoization — 函數(shù)記憶或函數(shù)緩存,是一種優(yōu)化技術(shù),用于許多編程語言中,以減少冗余、昂貴的函數(shù)調(diào)用。這是通過緩存函數(shù)的返回值來實(shí)現(xiàn)的。

以下是一個(gè)昂貴的函數(shù)調(diào)用,它以一種非常低效的方式找到數(shù)字的平方:

const inefficientSquare = (num) => {
  let total = 0
  for (let i = 0; i < num; i++) {
    for (let j = 0; j < num; j++) {
      total++
    }
  }
  return total
}

我們可以使用相同的值運(yùn)行此函數(shù),每次都需要一段時(shí)間才能執(zhí)行。

const start = new Date()
inefficientSquare(40000)
console.log(new Date() - start) // 1278

const start2 = new Date()
inefficientSquare(40000)
console.log(new Date() - start2) // 1245

每一次執(zhí)行,它都會(huì)重新計(jì)算!

為 memoize 編寫偽代碼

在編寫任何代碼之前,讓我們先通過 Memoizer 進(jìn)行推理。

  • 引用函數(shù)作為輸入
  • 返回一個(gè)函數(shù)
  • 創(chuàng)建某種形式的緩存以保存先前函數(shù)調(diào)用的結(jié)果
  • 以后調(diào)用函數(shù)時(shí),如果存在緩存結(jié)果,則返回緩存結(jié)果
  • 如果緩存的值不存在,則調(diào)用函數(shù)并將結(jié)果存儲(chǔ)在緩存中

編碼時(shí)間

這是上述偽代碼概述的實(shí)現(xiàn)。如引言中所述,這是次優(yōu)的,您不應(yīng)該在生產(chǎn)中使用它。我會(huì)解釋為什么!

// 引用函數(shù)
const memoize = (func) => {
  // 創(chuàng)建結(jié)果緩存
  const results = {}
  // 返回函數(shù)
  return (...args) => {
    // 為結(jié)果緩存創(chuàng)建鍵
    const argsKey = JSON.stringify(args)
    // 僅在沒有緩存值時(shí)執(zhí)行 func
    if (!results[argsKey]) {
      // 將函數(shù)調(diào)用結(jié)果存儲(chǔ)在緩存中
      results[argsKey] = func(...args)
    }
    // 返回緩存值
    return results[argsKey]
  }
}

這個(gè)實(shí)現(xiàn)會(huì)有一些缺點(diǎn),它使用 JSON.stringify 在結(jié)果緩存中創(chuàng)建鍵。而 JSON.stringify 最大問題是它不能序列化某些輸入,如函數(shù)和 Symbol(以及 JSON 中找不到的任何內(nèi)容)。

測試效果

const inefficientSquare = memoize((num) => {
  let total = 0
  for (let i = 0; i < num; i++) {
    for (let j = 0; j < num; j++) {
      total++
    }
  }
  return total
})

const start = new Date()
inefficientSquare(40000)
console.log(new Date() - start) // 1251

const start2 = new Date()
inefficientSquare(40000)
console.log(new Date() - start2) // 0

我們可以看到,在執(zhí)行第二次時(shí),輸入相同的內(nèi)容,它不需要時(shí)間重新計(jì)算;我們只是從一個(gè)對(duì)象中提取緩存的值。

注意:它可以節(jié)省計(jì)算時(shí)間,提高讀取性能,讀取速度更快。但它同時(shí)也會(huì)消耗更多的內(nèi)存來保存以前的結(jié)果(用內(nèi)存換區(qū)性能)。

應(yīng)用場景

它常用于解決同參數(shù)的多次執(zhí)行問題,大量的循環(huán)或遞歸調(diào)用的數(shù)學(xué)運(yùn)算或判斷。

以下是一個(gè)斐波那契數(shù)列示例:

const factorial = memoize((n) => (n <= 1 ? 1 : n * factorial(n - 1)))

factorial(5)

函數(shù)記憶僅在純函數(shù)中有效

函數(shù)記憶很棒,但僅在您的純函數(shù)中有效。換句話說,如果函數(shù)的返回值依賴于多個(gè)輸入,那么這些輸入的緩存值并不總是正確的。此外,如果您的函數(shù)有副作用,那么 memoizer 不會(huì)復(fù)制這些副作用,它只會(huì)返回最終返回的函數(shù)值。

更多資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容