JavaScript 語句執行順序
Javascript 是一種面向對象、單線程、腳本語言,其執行順序被稱為“事件循環”或“事件隊列”。
在理解 Javascript 執行順序之前,我們先要了解一些基礎知識,如:
1. JavaScript是單線程的 2. 函數調用會形成一個堆棧(執行上下文棧) 3. 事件循環機制決定了 JavaScript 的執行順序
當瀏覽器遇到 JavaScript 代碼時,它會對其進行解析和執行。JavaScript 是一門單線程語言,它只有一個執行線程,也就是說代碼是按照順序一個接一個地執行。
然而,這也意味著一些代碼可能會阻塞主線程導致頁面卡頓。所以我們需要了解 JavaScript 執行過程。
執行棧
在 JavaScript 中,每次函數調用都會形成一個“執行上下文”。這個執行上下文被放在一個調用棧中,稱之為“執行?!?。
function a() { console.log('a'); } function b() { console.log('b'); } function c() { a(); b(); } c();
代碼執行時,首先執行全局上下文,將其放到執行棧的底部。當我們執行c()
時,它會形成一個新的執行上下文,并被放置在棧的頂部。接下來,a()
函數會形成一個新的執行上下文,并被放置在棧的頂部。當 a() 函數執行完畢后,它會被彈出棧。然后執行b()
函數,同樣會形成一個新的執行上下文并被放置在棧的頂部。最后,當 b() 函數執行完畢后,它也會被彈出棧。到此,整個調用棧為空。
事件循環
事件循環是一種機制,用來處理異步代碼。JavaScript 引擎并不會等待異步代碼執行完成,而是將其加入到一個隊列中,并繼續執行下面的代碼。
console.log(1) setTimeout(() => console.log(2), 1000) console.log(3)
代碼執行順序應該是 1、3、2。因為在 setTimeout 函數中的代碼會被延遲執行,所以它被加入到隊列中。當主線程執行完隊列中的所有同步代碼后,它會檢查隊列是否有異步代碼需要執行,如果有,則執行該異步代碼。這就是事件循環的機制。
微任務和宏任務
在 JavaScript 中,異步代碼分為兩種類型,分別是“微任務”和“宏任務”。
在事件循環機制中,每次主線程執行完同步代碼后,會先執行所有的微任務,然后執行一個宏任務。
function a() { console.log('a1'); setTimeout(() => console.log('a2'), 0); Promise.resolve().then(() => console.log('a3')); console.log('a4'); } a(); setTimeout(() => console.log('b'), 0);
當代碼執行時,它會先執行函數 a() 并形成一個新的執行上下文。當 a() 函數執行setTimeout
和Promise
時,它們會被加入到時間隊列中。當主線程執行完所有同步代碼后,它會去檢查微任務隊列中是否有任務需要執行,如果有則執行。在這個例子中,微任務是一個 Promise,所以它會執行 Promise 的then()
方法并打印'a3'
。然后主線程執行完所有的微任務后,它會去宏任務隊列中找到下一個任務,也就是setTimeout
,然后打印'a2'
和'b'
。
總結
JavaScript 語句執行順序遵循事件循環機制。當瀏覽器遇到異步代碼時,它會將其加入到一個隊列中,然后繼續執行下面的代碼。當主線程執行完同步代碼后,它會去檢查隊列中是否有需要執行的異步代碼,如果有則執行。在異步代碼執行的過程中,由于異步代碼有微任務和宏任務之分,所以執行順序也不同。