本文是旨在深入研究JavaScript及其實(shí)際工作原理的系列文章中的第一篇:我們認(rèn)為通過(guò)了解JavaScript的構(gòu)建塊以及它們是如何工作的,將能夠編寫(xiě)更好的代碼和應(yīng)用程序。愛(ài)掏網(wǎng) - it200.com我們還將分享構(gòu)建 SeStHealsStad 時(shí)使用的一些經(jīng)驗(yàn)法則,這是一個(gè)輕量級(jí)的 JavaScript 應(yīng)用程序,必須保持健壯和高性能以保持競(jìng)爭(zhēng)力。愛(ài)掏網(wǎng) - it200.com
如 GitHut 統(tǒng)計(jì) 數(shù)據(jù)所示,在GitHub中的活動(dòng)存儲(chǔ)庫(kù)和總推送方面,JavaScript處于頂部。愛(ài)掏網(wǎng) - it200.com它也不落后于其他類(lèi)別。愛(ài)掏網(wǎng) - it200.com
如果項(xiàng)目越來(lái)越依賴(lài)于 JavaScript,這意味著開(kāi)發(fā)人員必須利用語(yǔ)言和生態(tài)系統(tǒng)提供的所有內(nèi)容,對(duì)內(nèi)部進(jìn)行更深入的了解,以便構(gòu)建出色的軟件。愛(ài)掏網(wǎng) - it200.com
事實(shí)證明,有很多開(kāi)發(fā)人員每天都在使用JavaScript,但卻不知道背后發(fā)生了什么。愛(ài)掏網(wǎng) - it200.com
概述
幾乎每個(gè)人都已經(jīng)聽(tīng)說(shuō)過(guò) V8 引擎,大多數(shù)人都知道 JavaScript 是單線(xiàn)程的,或者它使用的是回調(diào)隊(duì)列。愛(ài)掏網(wǎng) - it200.com
在本文中,我們將詳細(xì)介紹這些概念,并解釋 JavaScrip 實(shí)際如何運(yùn)行。愛(ài)掏網(wǎng) - it200.com通過(guò)了解這些細(xì)節(jié),你將能夠適當(dāng)?shù)乩盟峁┑?API 來(lái)編寫(xiě)更好的、非阻塞的應(yīng)用程序。愛(ài)掏網(wǎng) - it200.com
如果您對(duì)JavaScript還比較陌生,那么本文將幫助您理解為什么JavaScript與其他語(yǔ)言相比如此“怪異”。愛(ài)掏網(wǎng) - it200.com
如果你是一個(gè)有經(jīng)驗(yàn)的JavaScript開(kāi)發(fā)人員,希望它能讓您對(duì)每天使用的JavaScript運(yùn)行時(shí)的實(shí)際工作方式有一些新的見(jiàn)解。愛(ài)掏網(wǎng) - it200.com
JavaScript引擎
JavaScript引擎的一個(gè)流行示例是Google的V8引擎。愛(ài)掏網(wǎng) - it200.com例如,在Chrome和Node.js中使用V8引擎,下面是一個(gè)非常簡(jiǎn)化的視圖:
V8引擎由兩個(gè)主要部件組成:
emory Heap(內(nèi)存堆)?—?內(nèi)存分配地址的地方
Call Stack(調(diào)用堆棧) — 代碼執(zhí)行的地方
Runtime(運(yùn)行時(shí))
有些瀏覽器的 API 經(jīng)常被使用到(比如說(shuō):setTimeout),但是,這些 API 卻不是引擎提供的。愛(ài)掏網(wǎng) - it200.com那么,他們是從哪兒來(lái)的呢?事實(shí)上這里面實(shí)際情況有點(diǎn)復(fù)雜。愛(ài)掏網(wǎng) - it200.com
所以說(shuō)我們還有很多引擎之外的 API,我們把這些稱(chēng)為瀏覽器提供 API 稱(chēng)為 Web API,比如說(shuō) DOM、AJAX、setTimeout等等。愛(ài)掏網(wǎng) - it200.com
然后我們還擁有如此流行的事件循環(huán)和回調(diào)隊(duì)列。愛(ài)掏網(wǎng) - it200.com
調(diào)用棧
JavaScript是一種單線(xiàn)程編程語(yǔ)言,這意味著它只有一個(gè)調(diào)用堆棧。愛(ài)掏網(wǎng) - it200.com因此,它一次只能做一件事。愛(ài)掏網(wǎng) - it200.com
調(diào)用棧是一種數(shù)據(jù)結(jié)構(gòu),它記錄了我們?cè)诔绦蛑械奈恢谩?b class="xhide">愛(ài)掏網(wǎng) - it200.com如果我們運(yùn)行到一個(gè)函數(shù),它就會(huì)將其放置到棧頂,當(dāng)從這個(gè)函數(shù)返回的時(shí)候,就會(huì)將這個(gè)函數(shù)從棧頂彈出,這就是調(diào)用棧做的事情。愛(ài)掏網(wǎng) - it200.com
來(lái)個(gè)栗子:
當(dāng)程序開(kāi)始執(zhí)行的時(shí)候,調(diào)用棧是空的,然后,步驟如下:
每一個(gè)進(jìn)入調(diào)用棧的都稱(chēng)為調(diào)用幀。愛(ài)掏網(wǎng) - it200.com
這能清楚的知道當(dāng)異常發(fā)生的時(shí)候堆棧追蹤是怎么被構(gòu)造的,堆棧的狀態(tài)是如何的,讓我們看一下下面的代碼:
如果這發(fā)生在 Chrome 里(假設(shè)這段代碼實(shí)在一個(gè)名為 foo.js 的文件中),那么將會(huì)生成以下的堆棧追蹤:
"堆棧溢出",當(dāng)你達(dá)到調(diào)用棧最大的大小的時(shí)候就會(huì)發(fā)生這種情況,而且這相當(dāng)容易發(fā)生,特別是在你寫(xiě)遞歸的時(shí)候卻沒(méi)有全方位的測(cè)試它。愛(ài)掏網(wǎng) - it200.com我們來(lái)看看下面的代碼:
當(dāng)引擎開(kāi)始執(zhí)行這段代碼時(shí),它首先調(diào)用函數(shù)“foo”。愛(ài)掏網(wǎng) - it200.com然而,這個(gè)函數(shù)是遞歸的,并且在沒(méi)有任何終止條件的情況下開(kāi)始調(diào)用自己。愛(ài)掏網(wǎng) - it200.com因此,在執(zhí)行的每一步中,相同的函數(shù)都會(huì)被一次又一次地添加到調(diào)用堆棧中,如下所示:
然而,在某些時(shí)候,調(diào)用堆棧中的函數(shù)調(diào)用數(shù)量超過(guò)了調(diào)用堆棧的實(shí)際大小,瀏覽器決定采取行動(dòng),拋出一個(gè)錯(cuò)誤,它可能是這樣的:
在單個(gè)線(xiàn)程上運(yùn)行代碼很容易,因?yàn)槟悴槐靥幚碓诙嗑€(xiàn)程環(huán)境中出現(xiàn)的復(fù)雜場(chǎng)景——例如死鎖。愛(ài)掏網(wǎng) - it200.com
但是在一個(gè)線(xiàn)程上運(yùn)行也非常有限制,由于 JavaScript 只有一個(gè)調(diào)用堆棧,當(dāng)某段代碼運(yùn)行變慢時(shí)會(huì)發(fā)生什么"http://cdn.files.qdfuns.com/article/content/picture/202403/14/095110hbekddp655feee0r.jpg" width="462" height="288" target="_blank" class="loading_pic"
這并不是最好的用戶(hù)體驗(yàn),不是嗎?
那么,我們?cè)鯓硬拍茉诓蛔枞鸘I和不使瀏覽器失去響應(yīng)的情況下執(zhí)行大量代碼呢?解決方案是異步回調(diào)。愛(ài)掏網(wǎng) - it200.com
轉(zhuǎn)載至https://segmentfault.com/a/1190000017352941(JavaScript是如何工作的:引擎,運(yùn)行時(shí)和調(diào)用堆棧的概述!
) 博主為: https://segmentfault.com/u/minnanitkong(前端小智)
此文章僅為個(gè)人筆記記錄,不做任何商業(yè)用途