Web應用性能始終至關重要,尤其在網頁開發中,緩慢的頁面加載速度會直接導致用戶流失。作為專業的前端開發者,我們必須重視性能優化。許多傳統的網頁性能優化方法,例如減少請求次數、使用CDN以及避免編寫阻塞渲染的代碼,至今仍然有效。然而,隨著越來越多的Web應用使用JavaScript,驗證代碼效率變得至關重要。
假設您有一個功能正常的函數,但懷疑其效率不高,併計劃進行改進。如何證明這一假設?目前測試JavaScript函數性能的最佳實踐是什麼?通常,最佳方法是使用內置的performance.now()
函數,測量函數執行前後的時間。本文將討論如何測量代碼執行時間以及避免一些常見陷阱的技巧。
關鍵要點
- Web開發中的性能至關重要,可以通過驗證代碼速度來改進。測試JavaScript函數性能的最佳方法是使用內置的
performance.now()
函數來測量函數執行前後的時間。 - 高分辨率時間API提供了一個名為
now()
的函數,它返回一個DOMHighResTimeStamp
對象。這提供了對已過去時間的精確描述,使其比內置的Date
對象更精確。 - 測量函數執行時間時,小心不要意外地測量不重要的內容。確保在兩次
performance.now()
之間唯一測量的操作是所討論函數的執行。 - 只測量一次函數執行時間是一個常見錯誤。執行時間會根據各種因素而有很大差異,因此建議重複執行函數併計算平均時間或中位數時間。
- 比較不同函數的速度時,上下文很重要。函數的速度會根據其執行的特定任務而有所不同。因此,為了編寫更快的Web應用,重要的是進行測量和比較。
performance.now()
高分辨率時間API提供了一個名為now()
的函數,它返回一個DOMHighResTimeStamp
對象。這是一個浮點數,以毫秒為單位反映當前時間,精確到千分之一毫秒
。單獨來看,這個數字對您的分析並沒有多大價值,但是兩個這樣的數字之間的差值可以精確地描述已經過去了多少時間。除了比內置的Date
對象更精確之外,它也是“單調的”。這意味著,簡單來說,它不受系統(例如您的筆記本電腦操作系統)定期校正系統時間的影響。更簡單地說,定義Date
的兩個實例併計算差值並不能代表已過去的時間。 “單調”的數學定義是(對於函數或數量)以這樣一種方式變化,即它要么從不減少,要么從不增加
。另一種解釋方法是,試想一下在一年中時鐘向前或向後撥動的時間段內使用它。例如,當您所在國家的時鐘都同意為了最大限度地利用白天陽光而跳過一小時時。如果您要在時鐘撥回一小時之前創建一個Date
實例,然後在之後再創建一個Date
實例,查看差異,它會顯示類似“1小時3秒123毫秒”的內容。使用performance.now()
的兩個實例,差異將是“3秒123毫秒和456789千分之一毫秒”。在本節中,我不會詳細介紹此API。因此,如果您想了解更多信息並查看其使用的一些示例,我建議您閱讀文章《Discovering the High Resolution Time API》。既然您已經了解了高分辨率時間API是什麼以及如何使用它,讓我們深入探討一些潛在的陷阱。但在這樣做之前,讓我們定義一個名為makeHash()
的函數,我們將在本文的其餘部分使用它。
function makeHash(source) { var hash = 0; if (source.length === 0) return hash; for (var i = 0; i < source.length; i++) { var char = source.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; // Convert to 32bit integer } return hash; }
此函數的執行可以按如下所示進行測量:
var t0 = performance.now(); var result = makeHash('Peter'); var t1 = performance.now(); console.log('Took', (t1 - t0).toFixed(4), 'milliseconds to generate:', result);
如果您在瀏覽器中運行此代碼,您應該會看到類似以下內容:
<code>Took 0.2730 milliseconds to generate: 77005292</code>
下面顯示了此代碼的實時演示:
[此處應插入CodePen鏈接,由於我無法訪問外部網站,無法提供]
考慮到這個例子,讓我們開始討論。
陷阱 #1 – 意外測量不重要的內容
在上面的示例中,您可以注意到,在一次performance.now()
和另一次performance.now()
之間,我們唯一做的就是調用函數makeHash()
並將它的值賦給變量result
。這給了我們執行該函數所需的時間,而沒有其他任何內容。此測量也可以按如下所示進行:
var t0 = performance.now(); console.log(makeHash('Peter')); // 不好的主意! var t1 = performance.now(); console.log('Took', (t1 - t0).toFixed(4), 'milliseconds');
下面顯示了此代碼段的實時演示:
[此處應插入CodePen鏈接,由於我無法訪問外部網站,無法提供]
但在這種情況下,我們將測量調用函數makeHash('Peter')
所需的時間以及在控制台上發送和打印該輸出所需的時間。我們不知道這兩個操作分別需要多長時間。您只知道組合時間。此外,發送和打印輸出所需的時間會根據瀏覽器甚至當時瀏覽器中發生的情況而有很大差異。
也許您完全知道console.log
不可預測地慢。但是,執行多個函數也是同樣錯誤的,即使每個函數都不涉及任何I/O。例如:
function makeHash(source) { var hash = 0; if (source.length === 0) return hash; for (var i = 0; i < source.length; i++) { var char = source.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; // Convert to 32bit integer } return hash; }
同樣,我們不知道執行時間是如何分配的。是變量賦值、toLowerCase()
調用還是toString()
調用?
陷阱 #2 – 只測量一次
另一個常見錯誤是只進行一次測量,總結所花費的時間,並根據該時間得出結論。在不同時間,這很可能完全不同。執行時間很大程度上取決於各種因素:
- 編譯器預熱時間(例如,將代碼編譯成字節碼的時間)
- 主線程忙於處理我們沒有意識到正在進行的其他事情
- 您的計算機的CPU(s)忙於某些會減慢整個瀏覽器速度的事情
一種增量改進方法是重複執行該函數,如下所示:
var t0 = performance.now(); var result = makeHash('Peter'); var t1 = performance.now(); console.log('Took', (t1 - t0).toFixed(4), 'milliseconds to generate:', result);
下面顯示了此示例的實時演示:
[此處應插入CodePen鏈接,由於我無法訪問外部網站,無法提供]
這種方法的風險在於,我們的瀏覽器JavaScript引擎可能會進行次優化,這意味著第二次使用相同的輸入調用該函數時,它可以從記住第一個輸出中獲益,並簡單地再次使用它。為了解決這個問題,您可以使用許多不同的輸入字符串,而不是重複發送相同的輸入字符串(例如“Peter”)。顯然,使用不同輸入進行測試的問題在於,我們正在測量的函數自然會花費不同的時間。也許某些輸入會導致比其他輸入更長的執行時間。
陷阱 #3 – 過度依賴平均值
在上一節中,我們了解到重複運行某些內容是一個好習慣,理想情況下應該使用不同的輸入。但是,我們必須記住,不同輸入的問題在於,執行時間可能比所有其他輸入都長得多。因此,讓我們退一步,發送相同的輸入。假設我們發送相同的輸入十次,並且每次都打印出花費的時間。輸出可能如下所示:
function makeHash(source) { var hash = 0; if (source.length === 0) return hash; for (var i = 0; i < source.length; i++) { var char = source.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; // Convert to 32bit integer } return hash; }
請注意,第一次,數字與其他九次完全不同。這很可能是因為我們瀏覽器中的JavaScript引擎進行了一些次優化,需要一些預熱。我們幾乎無法避免這種情況,但我們可以考慮一些好的補救措施來防止錯誤的結論。一種方法是計算最後九次的時間平均值。另一種更實用的方法是收集所有結果併計算中位數。基本上,這是按順序排列的所有結果,並選擇中間的一個。這就是performance.now()
如此有用的地方,因為您可以得到一個可以隨意使用的數字。讓我們再試一次,但這次我們將使用一個中位數函數:
var t0 = performance.now(); var result = makeHash('Peter'); var t1 = performance.now(); console.log('Took', (t1 - t0).toFixed(4), 'milliseconds to generate:', result);
陷阱 #4 – 以可預測的順序比較函數
我們已經了解到,多次測量某些內容並取平均值總是一個好主意。此外,最後一個例子告訴我們,最好使用中位數而不是平均值。現在,實際上,測量函數執行時間的一個很好的用途是了解幾個函數中哪個更快。假設我們有兩個函數,它們接受相同類型的輸入並產生相同的結果,但在內部它們的工作方式不同。假設我們想要一個函數,如果某個字符串存在於其他字符串的數組中,則返回true或false,但這不區分大小寫。換句話說,我們不能使用Array.prototype.indexOf
,因為它不區分大小寫。這是一個這樣的實現:
<code>Took 0.2730 milliseconds to generate: 77005292</code>
我們立即註意到,這可以改進,因為haystack.forEach
循環總是遍歷所有元素,即使我們有一個早期匹配。讓我們嘗試使用一個好的舊for循環來編寫一個更好的版本。
var t0 = performance.now(); console.log(makeHash('Peter')); // 不好的主意! var t1 = performance.now(); console.log('Took', (t1 - t0).toFixed(4), 'milliseconds');
現在讓我們看看哪個更快。我們通過運行每個函數10次並收集所有測量結果來做到這一點:
var t0 = performance.now(); var name = 'Peter'; var result = makeHash(name.toLowerCase()).toString(); var t1 = performance.now(); console.log('Took', (t1 - t0).toFixed(4), 'milliseconds to generate:', result);
我們運行它並得到以下輸出:
var t0 = performance.now(); for (var i = 0; i < 10; i++) { makeHash('Peter'); } var t1 = performance.now(); console.log('Took', ((t1 - t0) / 10).toFixed(4), 'milliseconds to generate');
下面顯示了此示例的實時演示:
[此處應插入CodePen鏈接,由於我無法訪問外部網站,無法提供]
究竟發生了什麼?第一個函數快了三倍。這本不應該發生!
解釋很簡單,但很微妙。使用haystack.forEach
的第一個函數受益於瀏覽器JavaScript引擎中的一些低級優化,而當我們使用數組索引技術時,我們不會得到這些優化。它證明了我們的觀點:除非你測量它,否則你永遠不會知道!
結論
在我們試圖演示如何使用performance.now()
在JavaScript中獲得精確的執行時間時,我們偶然發現了一個基準測試場景,在這個場景中,我們的直覺與我們的經驗結果得出的結論恰恰相反。關鍵是,如果您想編寫更快的Web應用程序,您的JavaScript代碼需要進行優化。因為計算機(幾乎)是活生生的東西,所以它們是不可預測和令人驚訝的。知道我們的代碼改進會產生更快的執行速度的最可靠方法是測量和比較。我們永遠不知道哪段代碼更快,如果我們有多種方法來做同樣的事情,另一個原因是上下文很重要。在上一節中,我們執行了一個不區分大小寫的字符串搜索,在一個26個其他字符串中查找一個字符串。如果我們必須在100,000個其他字符串中查找一個字符串,那麼結論很可能會完全不同。上面的列表並不詳盡,因為還有更多需要注意的陷阱。例如,測量不切實際的場景或只在一個JavaScript引擎上進行測量。但可以肯定的是,對於想要編寫更快、更好的Web應用程序的JavaScript開發者來說,performance.now()
是一個巨大的財富。最後但並非最不重要的一點是,請記住,測量執行時間只會產生“更好代碼”的一個維度。還需要考慮內存和代碼複雜性。您呢?您是否曾經使用此函數來測試代碼的性能?如果沒有,您在此階段如何進行?請在下面的評論中分享您的想法。讓我們開始討論吧!
(FAQs部分略去,因為與上面內容高度重複,只需保留關鍵要點即可。)
以上是測量JavaScript功能&#x27;表現的詳細內容。更多資訊請關注PHP中文網其他相關文章!

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

理解JavaScript引擎內部工作原理對開發者重要,因為它能幫助編寫更高效的代碼並理解性能瓶頸和優化策略。 1)引擎的工作流程包括解析、編譯和執行三個階段;2)執行過程中,引擎會進行動態優化,如內聯緩存和隱藏類;3)最佳實踐包括避免全局變量、優化循環、使用const和let,以及避免過度使用閉包。

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

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

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

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

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

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


熱AI工具

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

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

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

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

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

WebStorm Mac版
好用的JavaScript開發工具

Dreamweaver Mac版
視覺化網頁開發工具

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