深入了解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
作用域,它有標識符
a、
b和
bar ## bar - 作用域,包含
c
標識符 仔細查看上面的程式碼,我們注意到函數
可以存取變數a 和b,因為它嵌套在foo
中。請注意,我們成功地儲存了函數 bar
及其運行環境。因此,我們說 bar
在 foo
的作用域上有一個閉包。 你可以在遺傳的背景下理解這一點,即個體有機會獲得並表現出遺傳特徵,即使是在他們當前的環境之外,這個邏輯突出了閉包的另一個因素,引出了我們的第二個主要概念。
透過接受其他函數作為參數或傳回其他函數的函數稱為高階函數。
閉包允許我們在封閉函數的外部呼叫內部函數,同時保持對封閉函數的詞法作用域的存取
讓我們對前面的範例中的程式碼進行一些調整,以解釋這一點。
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/imghw/default1.png" data-src="https://img.php.cn/upload/image/136/162/211/1604656285576117.jpg" 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)
所需的时间。结果如下:
哇! ! !这让人很惊讶,使用缓存的 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
函数与上面的例子进行比较时,结果如下:
memoizer
函数以 42,982,762 ops/sec 的速度提供了最快的解决方案,比之前考虑的解决方案速度要快 100%。
关于缓存,我们已经说明什么是缓存 、为什么要有缓存和如何实现缓存。现在我们来看看什么时候使用缓存。
何时使用缓存
当然,使用缓存效率是级高的,你现在可能想要缓存所有的函数,这可能会变得非常无益。以下几种情况下,适合使用缓存:
- 对于昂贵的函数调用,执行复杂计算的函数。
- 对于具有有限且高度重复输入范围的函数。
- 用于具有重复输入值的递归函数。
- 对于纯函数,即每次使用特定输入调用时返回相同输出的函数。
缓存库
总结
使用缓存方法 ,我们可以防止函数调用函数来反复计算相同的结果,现在是你把这些知识付诸实践的时候了。
更多编程相关知识,请访问:编程入门!!
以上是深入了解JavaScript 快取的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

瀏覽器快取影片在哪個資料夾在日常使用網路瀏覽器時,我們經常會觀看各種線上視頻,例如在YouTube上看音樂影片或在Netflix上觀看電影等。而這些影片在載入過程中會被瀏覽器快取下來,以便日後再次播放時能夠快速載入。那麼問題來了,這些快取的影片實際上儲存在哪個資料夾中呢?不同瀏覽器的快取視訊資料夾保存位置是不同的。以下我們將分別介紹幾種常見的瀏覽器以及它們

DNS(DomainNameSystem)是網際網路中用來將網域名稱轉換為對應IP位址的系統。在Linux系統中,DNS快取是一種將網域名稱和IP位址的映射關係儲存在本地的機制,可提高網域解析速度,減輕DNS伺服器的負擔。 DNS快取允許系統在之後存取相同網域名稱時快速檢索IP位址,而不必每次都向DNS伺服器發出查詢請求,從而提高網路效能和效率。本文不念將和大家一起探討如何在Linux上查看和刷新DNS緩存,以及相關的詳細內容和範例程式碼。 DNS快取的重要性在Linux系統中,DNS快取扮演關鍵的角色。它的存在

Guava缓存入门指南:加速你的应用程序Guava缓存是一个高性能的内存缓存库,它可以显著提高应用程序的性能。它提供了多种缓存策略,包括LRU(最近最少使用)、LFU(最近最不经常使用)和TTL(生存时间)。1.安装Guava缓存在你的项目中添加Guava缓存库的依赖。com.goog

標題:HTML檔案的快取機制及程式碼範例導語:在撰寫網頁時,我們常會遇到瀏覽器快取的問題。本文將詳細介紹HTML檔案的快取機制,並提供一些具體的程式碼範例,以幫助讀者更好地理解並應用此機制。一、瀏覽器快取原理在瀏覽器中,每當造訪一個網頁時,瀏覽器會先檢查快取中是否有該網頁的副本。如果有,則直接從快取獲取網頁內容,這就是瀏覽器快取的基本原理。瀏覽器快取機制的好處

CPU(中央處理器)、記憶體(隨機存取記憶體)以及快取之間存在著緊密的相互作用,它們合力構成了電腦系統的關鍵組成部分。它們之間的協調配合,確保了電腦的正常運作和高效性能。 CPU作為電腦的大腦,負責執行各種指令和資料處理;記憶體則用於臨時儲存資料和程序,提供了快速的讀寫存取速度;而快取則起到了緩衝作用,加快了資料的存取速度,提高了電腦的CPU是電腦的核心元件,負責執行各種指令、算術運算和邏輯操作。它被稱為電腦的"大腦",承擔著處理資料和執行任務的重要角色。記憶體是電腦中重要的儲存設備,

SpringBoot是一款廣受歡迎的Java框架,以其簡單易用和快速開發而聞名。然而,隨著應用程式的複雜性增加,效能問題可能會成為瓶頸。為了幫助您打造疾風般快速的springBoot應用,本文將分享一些實用的效能優化秘訣。優化啟動時間應用程式的啟動時間是使用者體驗的關鍵因素之一。 SpringBoot提供了多種最佳化啟動時間的途徑,例如使用快取、減少日誌輸出和最佳化類別路徑掃描。您可以透過在application.properties檔案中設定spring.main.lazy-initialization

PHPAPCu(替代php快取)是加速PHP應用程式的opcode快取和資料快取模組。理解其高級功能對於充分利用其潛力至關重要。 1.批次操作:APCu提供批次操作方法,可同時處理大量鍵值對。這對於大規模快取清除或更新非常有用。 //批次取得快取鍵$values=apcu_fetch(["key1","key2","key3"]);//批次清除快取鍵apcu_delete(["key1","key2","key3"]);2 .設定快取過期時間:APCu允許您為快取項目設定過期時間,以便在指定時間後自

瀏覽器快取的影片怎麼匯出來隨著網路的快速發展,影片已經成為人們日常生活中不可或缺的一部分。而在瀏覽網頁時,我們常常會遇到想要儲存或分享的影片內容,但是有時候我們卻無法找到影片檔案的來源,因為它們可能只存在於瀏覽器的快取中。那麼,如何匯出瀏覽器快取中的影片呢?本文將為您介紹幾種常用的方法。首先,我們需要明確一個概念,即瀏覽器快取。瀏覽器快取是瀏覽器為了提高用
