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