溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

JavaScript內(nèi)部原理是怎樣的

發(fā)布時間:2021-09-30 17:38:19 來源:億速云 閱讀:121 作者:柒染 欄目:web開發(fā)

本篇文章給大家分享的是有關(guān)JavaScript內(nèi)部原理是怎樣的,小編覺得挺實用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

簡介

Javascript  是一種奇怪語言,有些人喜歡它,有些人討厭它。它有許多獨特的機(jī)制,這些機(jī)制在其他流行語言中不存在,也沒有對應(yīng)的機(jī)制,還有突出明顯的就是代碼的執(zhí)行順序。

了解瀏覽器環(huán)境,它的組成以及它的工作原理會讓我們在編寫 JS 時更加自信,并為可能發(fā)生的潛在問題做好了充分的準(zhǔn)備。

JavaScript引擎

最流行的JavaScript引擎是V8,它是用c++編寫的,并被基于Chrome的瀏覽器使用,如Chrome、Opera甚至Edge?;旧?,這個引擎是一個將  JS 轉(zhuǎn)換成機(jī)器碼并在計算機(jī)的中央處理器(CPU)上執(zhí)行結(jié)果的程序。

編譯

當(dāng)瀏覽器加載 JS  文件時,V8的解析器將其轉(zhuǎn)換為一個抽象語法樹(AST)。該樹用于生成字節(jié)碼的解釋器。字節(jié)碼是一種可以通過編譯成非優(yōu)化的機(jī)器碼來執(zhí)行的機(jī)器碼的抽象。V8在主線程中執(zhí)行它,而優(yōu)化編譯器TurboFan在另一個線程中進(jìn)行一些優(yōu)化并生成優(yōu)化的機(jī)器碼。

這個管道稱為即時(JIT)編譯。

JavaScript內(nèi)部原理是怎樣的

調(diào)用堆棧

JavaScript  是一種單線程編程語言,只有一個調(diào)用堆棧。它意味著我們的代碼是同步執(zhí)行的。每當(dāng)一個函數(shù)運行時,它將在任何其他代碼運行之前完全運行。

當(dāng)V8調(diào)用 JS  函數(shù)時,它必須將運行時數(shù)據(jù)存儲在某個地方。調(diào)用堆棧是內(nèi)存中由堆棧幀組成的位置。每個堆棧幀對應(yīng)于一個尚未被調(diào)用函數(shù)。堆棧結(jié)構(gòu)由以下組成:

  • 局部變量

  • argument 參數(shù)

  • 返回地址

如果我們執(zhí)行一個函數(shù),V8 會將幀推到棧頂。當(dāng)我們從一個函數(shù)返回時,V8 會跳出幀。

JavaScript內(nèi)部原理是怎樣的

如上例所示,在每次函數(shù)調(diào)用時都會創(chuàng)建一個幀,并在每個return語句中將其刪除。

其他所有內(nèi)容都動態(tài)地分配到一個稱為堆的大型非結(jié)構(gòu)化內(nèi)存塊中。

堆(Heap)

有時V8在編譯時不知道對象變量需要多少內(nèi)存。此類數(shù)據(jù)的所有內(nèi)存分配都發(fā)生在堆中。退出分配內(nèi)存的函數(shù)后,堆上的對象繼續(xù)存在。

V8有一個內(nèi)置的垃圾收集器(GC)。垃圾收集是內(nèi)存管理的一種形式。它就像一個收集器,試圖釋放不再使用的對象占用的內(nèi)存。換句話說,當(dāng)一個變量失去所有引用時,GC將該內(nèi)存標(biāo)記為不可訪問并釋放它。

我們可以通過在Chrome開發(fā)工具中創(chuàng)建快照來研究堆。

JavaScript內(nèi)部原理是怎樣的

實例化的每個 JS 對象都分組在其構(gòu)造函數(shù)類下。括號中的分組表示不能直接調(diào)用的原生構(gòu)造函數(shù)。可以看到有很多(編譯代碼)和(系統(tǒng))實例,但也有一些傳統(tǒng)的  JS 對象,如Math、String、Array等。

瀏覽器運行時

V8可以根據(jù)標(biāo)準(zhǔn),同步地使用一個調(diào)用堆棧來執(zhí)行 JS  。但,我們需要渲染UI,需要處理用戶與UI的交互。此外,我們還需要在發(fā)出網(wǎng)絡(luò)請求時處理用戶交互,對此卻無能為力。當(dāng)所有代碼都是同步的時候,我們?nèi)绾螌崿F(xiàn)并發(fā)呢?  這還得感謝瀏覽器引擎。

瀏覽器引擎負(fù)責(zé)用 HTML 和 CSS 渲染頁面。在 Chrome 中它被稱為Blink。它是WebCore的一個分支,Blink  是一個布局、渲染和文檔對象模型(DOM)庫。Blink 是用 c++  中實現(xiàn)的,它提供了DOM元素和事件、XMLHttpRequest、fetch、setTimeout、setInterval等 Web api,這些api可以通過  JS 訪問。

我們一起思考下面帶有setTimeout(onTimeout, 0)的示例:

JavaScript內(nèi)部原理是怎樣的

可以看到,瀏覽器首先將f1()和f2()函數(shù)推入堆棧,然后執(zhí)行onTimeout。那么上面的示例如何工作?

并發(fā)性

setTimeout函數(shù)執(zhí)行后,瀏覽器引擎立即將setTimeout的回調(diào)函數(shù)放入一個事件表中。它是一個數(shù)據(jù)結(jié)構(gòu),將注冊的回調(diào)映射到事件,在我們的例子中是onTimeout函數(shù)映射到timeout事件。

一旦計時器到時,在本例中,我們將延遲設(shè)為0  ms,則立即觸發(fā)事件,并將onTimeout函數(shù)放入事件隊列(又名回調(diào)隊列,消息隊列或任務(wù)隊列)中。事件隊列是一種數(shù)據(jù)結(jié)構(gòu),由將來要處理的回調(diào)函數(shù)(任務(wù))組成。

最后且重要的是,事件循環(huán)(一個不斷運行的循環(huán))檢查調(diào)用堆棧是否為空。如果是,則執(zhí)行從事件隊列中添加的第一個回調(diào),從而移動到調(diào)用堆棧。

函數(shù)的處理將繼續(xù),直到調(diào)用堆棧再次為空。然后,事件循環(huán)將處理事件隊列中的下一個回調(diào)(如果有的話)。

JavaScript內(nèi)部原理是怎樣的

JavaScript內(nèi)部原理是怎樣的

注意onResolve1、onResolve2和onTimeout回調(diào)的執(zhí)行順序。

阻塞和非阻塞

簡單地說,所有 JS 代碼都被認(rèn)為是阻塞的。當(dāng) V8 忙于處理堆棧幀時,瀏覽器被卡住了,應(yīng)用程序的 UI 被阻塞。用戶將無法單擊、導(dǎo)航或滾動。直到 V8  完成它的工作,才會處理來自網(wǎng)絡(luò)請求的響應(yīng)。

想象一下,我們?nèi)绻跒g覽器中運行的程序中解析圖像。

JavaScript內(nèi)部原理是怎樣的

JavaScript內(nèi)部原理是怎樣的

在上面的示例中,事件循環(huán)被阻止。它無法處理事件/作業(yè)隊列中的回調(diào),因為調(diào)用堆棧包含這一幀。

Web API  為我們提供了通過異步回調(diào)來編寫非阻塞代碼的可能性。當(dāng)調(diào)用像setTimeout或fetch這樣的函數(shù)時,我們把所有的工作委托給c++原生代碼,它在一個單獨的線程中運行。一旦操作完成,回調(diào)就被放入事件隊列。同時,V8可以繼續(xù)執(zhí)行  JS 代碼。

使用這種并發(fā)模型,我們可以處理網(wǎng)絡(luò)請求、用戶與UI的交互等等,而不會阻塞 JS 執(zhí)行線程。


對于希望能夠解決復(fù)雜任務(wù)的每個開發(fā)人員來說,理解 JS  環(huán)境由什么組成是至關(guān)重要的。現(xiàn)在我們知道了異步JavaScript是如何工作的,調(diào)用堆棧、事件循環(huán)、事件隊列和作業(yè)隊列在其并發(fā)模型中的角色。

你可能已經(jīng)猜到的,在V8引擎和瀏覽器引擎后面還有很多工作要做。然而,我們大多數(shù)人只是需要對所有這些概念有一個基本的理解。

以上就是JavaScript內(nèi)部原理是怎樣的,小編相信有部分知識點可能是我們?nèi)粘9ぷ鲿姷交蛴玫降摹OM隳芡ㄟ^這篇文章學(xué)到更多知識。更多詳情敬請關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI