搜尋
首頁web前端js教程深入了解JavaScript 快取

深入了解JavaScript 快取

Nov 06, 2020 pm 05:55 PM
javascript快取

深入了解JavaScript 快取

隨著我們的應用程式的不斷增長並開始進行複雜的計算時,對速度的需求越來越高,所以流程的最佳化變得必不可少。當我們忽略這個問題時,我們最終的程序需要花費大量時間並在執行期間消耗大量的系統資源。

推薦教學:《JavaScript影片教學

快取是一種最佳化技術,透過儲存開銷大的函數執行的結果,並在相同的輸入再次出現時傳回已快取的結果,從而加快應用程式的速度。

如果這對你沒有多大意義,那也沒關係。本文深入解釋了為什麼需要進行緩存,緩存是什麼,如何實現以及何時應該使用緩存。

什麼是快取

快取是一種最佳化技術,透過儲存開銷大的函數執行的結果,並在相同的輸入再次出現時傳回已快取的結果,從而加快應用程式的速度。

在這一點上,我們很清楚,快取的目的是減少執行「昂貴的函數呼叫」所花費的時間和資源。

什麼是昂貴的函數呼叫?別搞混了,我們不是在這裡花錢。在電腦程式的上下文中,我們擁有的兩個主要資源是時間和記憶體。因此,一個昂貴的函數呼叫是指一個函數呼叫中,由於計算量大,在執行過程中大量佔用了電腦的資源和時間。

然而,就像對待金錢一樣,我們需要節約。為此,使用快取來儲存函數呼叫的結果,以便在將來的時間內快速方便地存取。

快取只是一個臨時的數據存儲,它保存數據,以便將來對該數據的請求能夠更快地處理。

因此,當一個昂貴的函數被呼叫一次時,結果被儲存在快取中,這樣,每當在應用程式中再次呼叫該函數時,結果就會從快取中非常快速地取出,而不需要重新進行任何計算。

為什麼快取很重要?

下面是一個實例,說明了快取的重要性:

想像一下,你正在公園裡讀一本封面很吸引人的新小說。每次一個人經過,他們都會被封面吸引,所以他們會問書名和作者。第一次被問到這個問題的時候,你翻開書,唸出書名和作者的名字。現在越來越多的人來這裡問同樣的問題。你是一個很好的人,所以你回答所有問題。

你會翻開封面,把書名和作者的名字一一告訴他,還是開始憑記憶回答?哪個能節省你更多的時間?

發現其中的相似之處了嗎?使用記憶法,當函數提供輸入時,它執行所需的計算並在返回值之前將結果儲存到快取中。如果將來接收到相同的輸入,它就不必一遍又一遍地重複,它只需要從快取(記憶體)中提供答案。

快取是怎麼運作的

JavaScript 中的快取的概念主要建立在兩個概念之上,它們分別是:

  • 閉包
  • 高階函數(傳回函數的函數)

#閉包

##閉包是函數和宣告該函數的詞法環境的組合。
不是很清楚? 我也這麼認為。

為了更好的理解,讓我們快速研究一下 JavaScript 中詞法作用域的概念,詞法作用域只是指程式設計師在編寫程式碼時指定的變數和區塊的物理位置。如下程式碼:

function foo(a) {
  var b = a + 2;
  function bar(c) {
    console.log(a, b, c);
  }
  bar(b * 2);
}

foo(3); // 3, 5, 10
從這段程式碼中,我們可以確定三個作用域:

    全域作用域(包含
  • foo 作為唯一識別碼)
  • foo 作用域,它有標識符abbar
  • ## bar
  • 作用域,包含c 標識符
  • 仔細查看上面的程式碼,我們注意到函數
foo

可以存取變數a 和b,因為它嵌套在foo 中。請注意,我們成功地儲存了函數 bar 及其運行環境。因此,我們說 barfoo 的作用域上有一個閉包。 你可以在遺傳的背景下理解這一點,即個體有機會獲得並表現出遺傳特徵,即使是在他們當前的環境之外,這個邏輯突出了閉包的另一個因素,引出了我們的第二個主要概念。

從函數傳回函數

透過接受其他函數作為參數或傳回其他函數的函數稱為高階函數。

閉包允許我們在封閉函數的外部呼叫內部函數,同時保持對封閉函數的詞法作用域的存取

讓我們對前面的範例中的程式碼進行一些調整,以解釋這一點。

function foo(){
  var a = 2;

  function bar() {
    console.log(a);
  }
  return bar;
}
var baz = foo();
baz();//2

注意函数 foo 如何返回另一个函数 bar。这里我们执行函数 foo 并将返回值赋给baz。但是在本例中,我们有一个返回函数,因此,baz 现在持有对 foo 中定义的bar 函数的引用。

最有趣的是,当我们在 foo 的词法作用域之外执行函数 baz 时,仍然会得到 a 的值,这怎么可能呢?

请记住,由于闭包的存在,bar 总是可以访问 foo 中的变量(继承的特性),即使它是在 foo 的作用域之外执行的。

案例研究:斐波那契数列

斐波那契数列是什么?

斐波那契数列是一组数字,以1 或 0 开头,后面跟着1,然后根据每个数字等于前两个数字之和规则进行。如

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, …

或者

1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, …

挑战:编写一个函数返回斐波那契数列中的 n 元素,其中的序列是:

[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, …]

知道每个值都是前两个值的和,这个问题的递归解是:

function fibonacci(n) {
  if (n <p>确实简洁准确!但是,有一个问题。请注意,当 <code>n</code> 的值到终止递归之前,需要做大量的工作和时间,因为序列中存在对某些值的重复求值。</p><p>看看下面的图表,当我们试图计算 <code>fib(5)</code>时,我们注意到我们反复地尝试在不同分支的下标 <code>0,1,2,3</code> 处找到 Fibonacci 数,这就是所谓的冗余计算,而这正是缓存所要消除的。</p><p><img src="/static/imghwm/default1.png" data-src="https://img.php.cn/upload/image/136/162/211/1604656285576117.jpg?x-oss-process=image/resize,p_40" class="lazy" title="1604656285576117.jpg" alt="深入了解JavaScript 快取"></p><pre class="brush:php;toolbar:false">function fibonacci(n, memo) {
  memo = memo || {}
  if (memo[n]) {
    return memo[n]
  }
  if (n <p>在上面的代码片段中,我们调整函数以接受一个可选参数 <code>memo</code>。我们使用 <code>memo</code> 对象作为缓存来存储斐波那契数列,并将其各自的索引作为键,以便在执行过程中稍后需要时检索它们。</p><pre class="brush:php;toolbar:false">memo = memo || {}

在这里,检查是否在调用函数时将 memo 作为参数接收。如果有,则初始化它以供使用;如果没有,则将其设置为空对象。

if (memo[n]) {
  return memo[n]
}

接下来,检查当前键 n 是否有缓存值,如果有,则返回其值。

和之前的解一样,我们指定了 n 小于等于 1 时的终止递归。

最后,我们递归地调用n值较小的函数,同时将缓存值(memo)传递给每个函数,以便在计算期间使用。这确保了在以前计算并缓存值时,我们不会第二次执行如此昂贵的计算。我们只是从 memo 中取回值。

注意,我们在返回缓存之前将最终结果添加到缓存中。

使用 JSPerf 测试性能

可以使用些链接来性能测试。在那里,我们运行一个测试来评估使用这两种方法执行fibonacci(20) 所需的时间。结果如下:

深入了解JavaScript 快取

哇! ! !这让人很惊讶,使用缓存的 fibonacci 函数是最快的。然而,这一数字相当惊人。它执行 126,762 ops/sec,这远远大于执行 1,751 ops/sec 的纯递归解决方案,并且比较没有缓存的递归速度大约快 99%。

注:“ops/sec”表示每秒的操作次数,就是一秒钟内预计要执行的测试次数。

现在我们已经看到了缓存在函数级别上对应用程序的性能有多大的影响。这是否意味着对于应用程序中的每个昂贵函数,我们都必须创建一个修改后的变量来维护内部缓存?

不,回想一下,我们通过从函数返回函数来了解到,即使在外部执行它们,它们也会导致它们继承父函数的范围,这使得可以将某些特征和属性从封闭函数传递到返回的函数。

使用函数的方式

在下面的代码片段中,我们创建了一个高阶的函数 memoizer。有了这个函数,将能够轻松地将缓存应用到任何函数。

function memoizer(fun) {
  let cache = {}
  return function (n) {
    if (cache[n] != undefined) {
      return cache[n]
    } else {
      let result = fun(n)
      cache[n] = result
      return result
    }
  }
}

上面,我们简单地创建一个名为 memoizer 的新函数,它接受将函数 fun 作为参数进行缓存。在函数中,我们创建一个缓存对象来存储函数执行的结果,以便将来使用。

memoizer 函数中,我们返回一个新函数,根据上面讨论的闭包原则,这个函数无论在哪里执行都可以访问 cache

在返回的函数中,我们使用 if..else 语句检查是否已经有指定键(参数) n 的缓存值。如果有,则取出并返回它。如果没有,我们使用函数来计算结果,以便缓存。然后,我们使用适当的键 n 将结果添加到缓存中,以便以后可以从那里访问它。最后,我们返回了计算结果。

很顺利!

要将 memoizer 函数应用于最初递归的 fibonacci 函数,我们调用 memoizer 函数,将 fibonacci 函数作为参数传递进去。

const fibonacciMemoFunction = memoizer(fibonacciRecursive)

测试 memoizer 函数

当我们将 memoizer 函数与上面的例子进行比较时,结果如下:

深入了解JavaScript 快取

memoizer 函数以 42,982,762 ops/sec 的速度提供了最快的解决方案,比之前考虑的解决方案速度要快 100%。

关于缓存,我们已经说明什么是缓存 、为什么要有缓存和如何实现缓存。现在我们来看看什么时候使用缓存。

何时使用缓存

当然,使用缓存效率是级高的,你现在可能想要缓存所有的函数,这可能会变得非常无益。以下几种情况下,适合使用缓存:

  • 对于昂贵的函数调用,执行复杂计算的函数。
  • 对于具有有限且高度重复输入范围的函数。
  • 用于具有重复输入值的递归函数。
  • 对于纯函数,即每次使用特定输入调用时返回相同输出的函数。

缓存库

总结

使用缓存方法 ,我们可以防止函数调用函数来反复计算相同的结果,现在是你把这些知识付诸实践的时候了。

更多编程相关知识,请访问:编程入门!!

以上是深入了解JavaScript 快取的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文轉載於:segmentfault。如有侵權,請聯絡admin@php.cn刪除
Python vs. JavaScript:社區,圖書館和資源Python vs. JavaScript:社區,圖書館和資源Apr 15, 2025 am 12:16 AM

Python和JavaScript在社區、庫和資源方面的對比各有優劣。 1)Python社區友好,適合初學者,但前端開發資源不如JavaScript豐富。 2)Python在數據科學和機器學習庫方面強大,JavaScript則在前端開發庫和框架上更勝一籌。 3)兩者的學習資源都豐富,但Python適合從官方文檔開始,JavaScript則以MDNWebDocs為佳。選擇應基於項目需求和個人興趣。

從C/C到JavaScript:所有工作方式從C/C到JavaScript:所有工作方式Apr 14, 2025 am 12:05 AM

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

JavaScript引擎:比較實施JavaScript引擎:比較實施Apr 13, 2025 am 12:05 AM

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

超越瀏覽器:現實世界中的JavaScript超越瀏覽器:現實世界中的JavaScriptApr 12, 2025 am 12:06 AM

JavaScript在現實世界中的應用包括服務器端編程、移動應用開發和物聯網控制:1.通過Node.js實現服務器端編程,適用於高並發請求處理。 2.通過ReactNative進行移動應用開發,支持跨平台部署。 3.通過Johnny-Five庫用於物聯網設備控制,適用於硬件交互。

使用Next.js(後端集成)構建多租戶SaaS應用程序使用Next.js(後端集成)構建多租戶SaaS應用程序Apr 11, 2025 am 08:23 AM

我使用您的日常技術工具構建了功能性的多租戶SaaS應用程序(一個Edtech應用程序),您可以做同樣的事情。 首先,什麼是多租戶SaaS應用程序? 多租戶SaaS應用程序可讓您從唱歌中為多個客戶提供服務

如何使用Next.js(前端集成)構建多租戶SaaS應用程序如何使用Next.js(前端集成)構建多租戶SaaS應用程序Apr 11, 2025 am 08:22 AM

本文展示了與許可證確保的後端的前端集成,並使用Next.js構建功能性Edtech SaaS應用程序。 前端獲取用戶權限以控制UI的可見性並確保API要求遵守角色庫

JavaScript:探索網絡語言的多功能性JavaScript:探索網絡語言的多功能性Apr 11, 2025 am 12:01 AM

JavaScript是現代Web開發的核心語言,因其多樣性和靈活性而廣泛應用。 1)前端開發:通過DOM操作和現代框架(如React、Vue.js、Angular)構建動態網頁和單頁面應用。 2)服務器端開發:Node.js利用非阻塞I/O模型處理高並發和實時應用。 3)移動和桌面應用開發:通過ReactNative和Electron實現跨平台開發,提高開發效率。

JavaScript的演變:當前的趨勢和未來前景JavaScript的演變:當前的趨勢和未來前景Apr 10, 2025 am 09:33 AM

JavaScript的最新趨勢包括TypeScript的崛起、現代框架和庫的流行以及WebAssembly的應用。未來前景涵蓋更強大的類型系統、服務器端JavaScript的發展、人工智能和機器學習的擴展以及物聯網和邊緣計算的潛力。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
4 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
4 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
4 週前By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
1 個月前By尊渡假赌尊渡假赌尊渡假赌

熱工具

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。

Dreamweaver Mac版

Dreamweaver Mac版

視覺化網頁開發工具

Safe Exam Browser

Safe Exam Browser

Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。