JavaScript 幕后工作原理:JS 引擎和運行時

你可能知道你的代碼以某種方式在瀏覽器中編譯和執(zhí)行,以顯示你所創(chuàng)建的漂亮的 Web 應(yīng)用。但你是否知道有哪些組件為實現(xiàn)輸出發(fā)揮了作用?
讓我們深入了解一下幕后的 JavaScript,你無法確切看到的抽象部分。
為什么一個看似抽象的主題對你來說很重要?對 JavaScript 內(nèi)部工作原理的理解使你能夠超越表面水平,從更深的角度來探索語言。
它提供了關(guān)于該語言的背景信息,以及 JavaScript 引擎如何優(yōu)化代碼。這將給你一些重要的基礎(chǔ)知識,這些知識塑造了你寫代碼的方式。它還能幫助你寫出更高效、可擴展和可維護的代碼。
JavaScript 引擎
簡單地說,JavaScript 引擎是一個解釋 JavaScript 代碼的計算機程序。該引擎負(fù)責(zé)執(zhí)行代碼。
每個主流的瀏覽器都有一個可以執(zhí)行 JavaScript 代碼的 JavaScript 引擎。最流行的是谷歌瀏覽器 Chrome 的 V8 引擎。谷歌的 V8 為 Chrome 和 Node.js 提供支持,Node.js 是一個后端 JavaScript 運行環(huán)境,用于構(gòu)建服務(wù)器端應(yīng)用程序。
其他主要的瀏覽器引擎包括:
由 Mozilla 為 Firefox 開發(fā)的 SpiderMonkey 為 Safari 瀏覽器提供支持的 JavaScriptCore 為 Internet Explorer 提供支持的 Chakra
任何 JavaScript 引擎通常都包含一個調(diào)用棧和一個堆。調(diào)用棧是代碼執(zhí)行的地方。堆是一個非結(jié)構(gòu)化的內(nèi)存池,用于存儲應(yīng)用程序所需的所有對象。
由于計算機的處理器只能理解二進制、0 和 1,因此必須將代碼轉(zhuǎn)換為 0 和 1。
當(dāng)代碼片段進入引擎時,代碼首先被解析,也就是被讀取。該代碼隨后被解析為叫作抽象語法樹(AST)的數(shù)據(jù)結(jié)構(gòu)。生成的樹被用來來創(chuàng)建機器代碼。
執(zhí)行發(fā)生在使用執(zhí)行上下文的JavaScript引擎調(diào)用棧中。這是執(zhí)行 JavaScript 代碼的環(huán)境。
JavaScript 運行時
將 JavaScript 運行時視為包含運行 JavaScript 所需的所有組件的房子。這個房子包括 JavaScript 引擎、Web API 和回調(diào)隊列。
Web APIs 是提供給引擎的功能,但不是 JavaScript 語言的一部分。引擎可以通過瀏覽器訪問它們,并有助于訪問數(shù)據(jù)或增強瀏覽器功能。示例是文檔對象模型(DOM)和獲取 API。
回調(diào)隊列包括準(zhǔn)備好執(zhí)行的回調(diào)函數(shù)。回調(diào)隊列確?;卣{(diào)以先進先出(FIFO)方法執(zhí)行,并在堆棧為空時將其傳遞到堆棧中。
瀏覽器運行時和 Node.js 是運行時環(huán)境的例子。
當(dāng) JavaScript 在 Web 瀏覽器中執(zhí)行時,它是在瀏覽器的運行時環(huán)境中運行的。瀏覽器運行時環(huán)境提供對 DOM 的訪問,從而實現(xiàn)了與網(wǎng)頁元素的交互,處理事件,以及對頁面結(jié)構(gòu)的操作。
Node.js 提供了一個服務(wù)器端運行時環(huán)境,用于在瀏覽器外部執(zhí)行 JavaScript。因為它在瀏覽器外部執(zhí)行 JavaScript,所以它不能訪問 Web API。Node.js 運行時環(huán)境將其替換為叫作 C++ 綁定和線程池的東西。
JavaScript 的優(yōu)化策略
現(xiàn)代 JavaScript 引擎有一些優(yōu)化策略,以提高代碼執(zhí)行的性能。這些優(yōu)化在執(zhí)行過程中是動態(tài)發(fā)生的。讓我們來看看其中一些策略。
即時編譯
涉及到將 JavaScript 代碼轉(zhuǎn)換成機器代碼的過程是使用編譯和解釋進行的。
在編譯中,整個源代碼被一次性轉(zhuǎn)換成機器代碼,并寫入二進制文件,由計算機執(zhí)行。
相反,在解釋期間,解釋器逐行解釋源代碼,遇到一行就執(zhí)行它。
JavaScript 曾經(jīng)是一種解釋型語言,但解釋型語言與編譯型語言相比速度較慢。
為了優(yōu)化 Web 應(yīng)用程序的性能,JavaScript 結(jié)合了編譯和解釋。這叫作即時編譯。該方法一次性將全部代碼編譯成機器碼并執(zhí)行。
即時編譯涉及與常規(guī)編譯相同的兩個過程,但這里的機器代碼沒有寫入二進制文件。代碼也是在編譯后立即執(zhí)行的。
這對 JavaScript 中的代碼執(zhí)行速度產(chǎn)生了重大影響。所以希望這有助于消除 JavaScript 是一種純解釋型語言的觀念。
為了完全優(yōu)化 JavaScript 代碼,引擎首先創(chuàng)建一個未優(yōu)化版本的機器代碼,以便它可以立即開始執(zhí)行。同時,代碼被重新優(yōu)化并在當(dāng)前運行的程序執(zhí)行的后臺重新編譯。這是多次進行的,以產(chǎn)生最終的、最優(yōu)化的版本。
解析、編譯和執(zhí)行的過程發(fā)生在引擎中無法從代碼訪問的一些特殊線程中。
什么是內(nèi)聯(lián)?
內(nèi)聯(lián)是 JavaScript 用來提高性能和速度的另一種優(yōu)化技術(shù)。
function?add(a,?b)?{
??return?a?+?b;
}
let?result?=?0;
result?=?result?+?5;
result?=?result?+?3;
console.log(result);?//
在這個片段中,原 add() 函數(shù)沒有被直接調(diào)用。相反,函數(shù) return a + b; 里面的代碼被插入到調(diào)用位置。
這種優(yōu)化特別針對那些被反復(fù)調(diào)用的函數(shù)。JavaScript 引擎會像正常情況下那樣運行該函數(shù)。但由于該函數(shù)經(jīng)常被調(diào)用,引擎會在調(diào)用位置用函數(shù)的實際代碼替換函數(shù)調(diào)用。這有助于防止多次函數(shù)調(diào)用,提高性能。
性能方面的考慮
有幾個因素會影響你的 Web 應(yīng)用程序的性能。由于 JavaScript 引擎采用了一些策略來確保優(yōu)化,也有一些最佳實踐需要開發(fā)人員考慮,以實現(xiàn)高效的執(zhí)行。
諸如盡量減少 DOM 操作和減少函數(shù)調(diào)用等技術(shù)可以提高代碼性能。
對 DOM 的頻繁訪問和互動會減慢網(wǎng)頁的渲染速度,導(dǎo)致性能滯后。既然你不能完全避免與 DOM 的交互,你可以通過批量 DOM 更新來減少交互,以降低開銷。
此外,減少函數(shù)調(diào)用可以使性能提高一個檔次。通過減少函數(shù)調(diào)用,你可以精簡你的代碼,使其更有效率,使你的 JavaScript 應(yīng)用程序更快、響應(yīng)更靈敏。
//?帶有不必要的函數(shù)調(diào)用的低效代碼
function?calculateTotal(a,?b,?c)?{
??return?addNumbers(a,?b)?+?multiplyNumbers(c,?b);
}
function?addNumbers(x,?y)?{
??return?x?+?y;
}
function?multiplyNumbers(x,?y)?{
??return?x?*?y;
}
//?改進代碼,減少函數(shù)調(diào)用
function?calculateTotal(a,?b,?c)?{
??const?sum?=?a?+?b;
??return?sum?+?c?*?b;
}
console.log(calculateTotal(2,?3,?4));?//?Output:?23
在低效代碼中,calculateTotal() 函數(shù)對 addNumbers() 和 multiplyNumbers() 進行單獨的函數(shù)調(diào)用。這導(dǎo)致了函數(shù)調(diào)用的開銷。
在改進后的代碼中,通過在 calculateTotal() 函數(shù)中直接執(zhí)行加法和乘法操作,減少了函數(shù)調(diào)用。通過減少函數(shù)調(diào)用,代碼變得更加有效,并提高了執(zhí)行速度。
未來 JavaScript 的發(fā)展和趨勢
JavaScript 引擎和運行時環(huán)境將繼續(xù)得到改進和提高。這些變化都是為了提高 Web 應(yīng)用的性能。
其中一個進步就是 WebAssembly 的崛起。WebAssembly 給 Web 應(yīng)用帶來了接近原生的性能,并支持多種語言。它為性能優(yōu)化和執(zhí)行速度帶來了新的可能性。
對 JavaScript 開發(fā)人員來說,重要的是要跟上這些趨勢,并相應(yīng)地調(diào)整新的編碼最佳實踐。
總結(jié)
你的 JavaScript 代碼是如何被解析的,直到它呈現(xiàn)出一個功能性的 Web 應(yīng)用,這其中涉及到很多過程。
這篇文章對主要概念進行了高層次的概述。它解釋了JavaScript引擎如何執(zhí)行代碼、運行時和它的組件。它還繼續(xù)解釋了優(yōu)化策略并強調(diào)了性能方面的考慮。
了解 JavaScript 是如何在幕后運行的,可以塑造開發(fā)人員處理問題和編寫更有效代碼的方式。它還可以幫助開發(fā)者在學(xué)習(xí)曲線上保持領(lǐng)先,并輕松適應(yīng) JavaScript 功能的未來變化。
為了更深入地學(xué)習(xí),你可以訪問這些資源:
執(zhí)行環(huán)境 事件循環(huán) 微任務(wù)隊列
Happy coding!
原文鏈接:https://www.freecodecamp.org/news/how-javascript-works-behind-the-scenes/
作者:Esther Christopher
譯者:Chengjun.L
