摘要:執(zhí)行棧所有的代碼在運行時都是在執(zhí)行上下文中進行的。引擎執(zhí)行棧頂?shù)暮瘮?shù),執(zhí)行完畢,彈出當前執(zhí)行上下文。但是這里我們也可以用執(zhí)行棧來解釋。
這是 JavaScript 系列的第 3 篇。
引例首先來看一個引例:
function foo() { console.log("1"); bar(); console.log("3"); } function bar() { console.log("2"); } foo();
這段代碼將從上往下依次執(zhí)行,并輸出 "1", "2", "3"。
我們可以看到,bar 函數(shù)的執(zhí)行順序似乎和它定義的順序沒有關系。為什么呢?這你就得弄懂執(zhí)行棧了。
執(zhí)行棧所有的 JS 代碼在運行時都是在執(zhí)行上下文中進行的。執(zhí)行上下文是一個抽象的概念,JS 中有三種執(zhí)行上下文:
全局執(zhí)行上下文,默認的,在瀏覽器中是 window 對象,并且 this 在非嚴格模式下指向它。
函數(shù)執(zhí)行上下文,JS 的函數(shù)每當被調用時會創(chuàng)建一個上下文。
Eval 執(zhí)行上下文,eval 函數(shù)會產(chǎn)生自己的上下文,這里不討論。
通常,我們的代碼中都不止一個上下文,那這些上下文的執(zhí)行順序應該是怎樣的?從上往下依次執(zhí)行?
棧,是一種數(shù)據(jù)結構,具有先進后出的原則。JS 中的執(zhí)行棧就具有這樣的結構,當引擎第一次遇到 JS 代碼時,會產(chǎn)生一個全局執(zhí)行上下文并壓入執(zhí)行棧,每遇到一個函數(shù)調用,就會往棧中壓入一個新的上下文。引擎執(zhí)行棧頂?shù)暮瘮?shù),執(zhí)行完畢,彈出當前執(zhí)行上下文。
以引例來說明。當 foo() 函數(shù)被調用,將 foo 函數(shù)的執(zhí)行上下文壓入執(zhí)行棧,接著執(zhí)行輸出 ‘1’;當 bar() 函數(shù)被調用,將 bar 函數(shù)的執(zhí)行上下文壓入執(zhí)行棧,接著執(zhí)行輸出 ‘2’;bar() 執(zhí)行完畢,被彈出執(zhí)行棧,foo() 函數(shù)接著執(zhí)行,輸出 ‘3’;foo() 函數(shù)執(zhí)行完畢,被彈出執(zhí)行棧。
那現(xiàn)在來看這個例子:
var count = 0; function foo(count) { count += 1; console.log(count); } foo(count); // 1 foo(count); // 1
我們用執(zhí)行棧來理解一下,函數(shù)每次被調用都會產(chǎn)生新的執(zhí)行上下文,并被壓入執(zhí)行棧,執(zhí)行完畢后當前上下文就會被彈出執(zhí)行棧。所以第一次調用應該返回 1,第二次調用也應該返回 1,第 n 次調用都應該返回 1。
你理解了嗎?那再來看一個例子:
var count = 0; function foo() { count += 1; console.log(count); } foo(count); // 1 foo(count); // 2
WTF?這個例子和上一個的區(qū)別是這里 foo 函數(shù)沒有指定形參。而這個例子其實就是通常說的函數(shù)內部沒有使用 var 聲明的變量,都會被當做全局變量(非嚴格模式)。
但是這里我們也可以用執(zhí)行棧來解釋。
函數(shù)的形參屬于函數(shù)執(zhí)行上下文,所以當指定這個形參后,它就隨著函數(shù)被調用而新建,隨著函數(shù)銷毀而銷毀。如果不指定這個形參,上一篇文章已經(jīng)介紹過作用域鏈的概念,就會沿著作用域鏈找到全局變量 count,它屬于全局執(zhí)行上下文,這個時候再去調用 foo() 函數(shù)就會讀寫這個全局變量。
每個 foo() 函數(shù)調用后,給 count 加一,然后被彈出執(zhí)行棧,而全局執(zhí)行上下文的生命周期將伴隨著整個程序,所以第一次調用打印 1,第二次調用打印 2,第 n 次調用打印 n。
是不是很奇妙呢?隨著學習的深入,你會發(fā)現(xiàn) JavaScript 的奇妙遠不止于此。
小結執(zhí)行棧屬于 JavaScript 中基礎的概念,它與作用域、作用域鏈、執(zhí)行上下文、變量對象/活動對象的聯(lián)系都非常緊密。
文章首發(fā)于微信公眾號,理解 JavaScript 執(zhí)行棧
歡迎關注我的公眾號 cameraee,一起交流學習。
JavaScript 系列文章分析 JavaScript 的數(shù)據(jù)類型與變量
理解 JavaScript 作用域
正在更新...
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/99948.html
摘要:當函數(shù)結束,將會被從調用棧移出。事件循環(huán)事件循環(huán)的責任就是查看調用棧并確定調用棧是否為空。事件循環(huán)會再次檢查調用棧是否為空,如果為空的話,它會把事件回調壓入棧中,然后回調函數(shù)則被執(zhí)行。 寫在文章前 這篇文章是翻譯自Sukhjinder Arora的Understanding Asynchronous JavaScript。這篇文章描述了異步和同步JavaScript是如何在運行環(huán)境中,...
摘要:此事件隊列的美妙之處在于它只是函數(shù)等待被調用和移動到調用棧的一個臨時存放區(qū)域。在事件循環(huán)不斷監(jiān)視調用棧是否為空現(xiàn)在確實是空的時候調用創(chuàng)建一個新的調用棧來執(zhí)行代碼。在執(zhí)行完之后進入了一個新的狀態(tài)這個狀態(tài)調用棧為空事件記錄表為空事件隊列也為空。 這篇文章是對個人認為講解 JavaScript 事件循環(huán)比較清楚的一篇英文文章的簡單翻譯,原文地址是http://altitudelabs.com...
摘要:主線程會暫時存儲等異步操作,直接向下執(zhí)行,當某個異步事件觸發(fā)時,再通知主線程執(zhí)行相應的回調函數(shù),通過這種機制,避免了單線程中異步操作耗時對后續(xù)任務的影響。 背景 在研究js的異步的實現(xiàn)方式的時候,發(fā)現(xiàn)了JavaScript 中的 macrotask 和 microtask 的概念。在查閱了一番資料之后,對其中的執(zhí)行機制有所了解,下面整理出來,希望可以幫助更多人。 先了解一下js的任務執(zhí)...
摘要:執(zhí)行上下文和執(zhí)行棧是中關鍵概念之一,是難點之一。理解執(zhí)行上下文和執(zhí)行棧同樣有助于理解其他的概念如提升機制作用域和閉包等。函數(shù)執(zhí)行完成,函數(shù)的執(zhí)行上下文出棧,并且被銷毀。 前言 如果你是一名 JavaScript 開發(fā)者,或者想要成為一名 JavaScript 開發(fā)者,那么你必須知道 JavaScript 程序內部的執(zhí)行機制。執(zhí)行上下文和執(zhí)行棧是JavaScript中關鍵概念之一,是Ja...
摘要:執(zhí)行上下文和執(zhí)行棧是中關鍵概念之一,是難點之一。理解執(zhí)行上下文和執(zhí)行棧同樣有助于理解其他的概念如提升機制作用域和閉包等。函數(shù)執(zhí)行完成,函數(shù)的執(zhí)行上下文出棧,并且被銷毀。 前言 如果你是一名 JavaScript 開發(fā)者,或者想要成為一名 JavaScript 開發(fā)者,那么你必須知道 JavaScript 程序內部的執(zhí)行機制。執(zhí)行上下文和執(zhí)行棧是JavaScript中關鍵概念之一,是Ja...
閱讀 2079·2023-04-25 21:11
閱讀 2968·2021-09-30 09:47
閱讀 2280·2021-09-24 09:48
閱讀 4438·2021-08-23 09:43
閱讀 900·2019-08-30 15:54
閱讀 567·2019-08-28 18:01
閱讀 1404·2019-08-27 10:55
閱讀 594·2019-08-27 10:55