返回

围观一道JavaScript智力题!真没想到,是这样解题的!

前端

JavaScript 智力題:釐清 setTimeout() 的奧秘

各位JavaScript愛好者,準備好迎接一場大腦激盪之旅吧!我們將深入探討一段JavaScript程式碼,揭開非同步程式設計的神秘面紗,並找出令人費解的 setTimeout() 函數的秘密。

程式碼剖析

讓我們仔細檢視以下程式碼片段:

var i = 0;
setTimeout(function() {
  console.log(i);
}, 1000);
i++;
console.log(i);

這段程式碼乍看之下看似簡單,但它卻隱藏著一個微妙的邏輯陷阱,可能會讓許多開發人員摸不著頭緒。讓我們逐行解剖這個程式碼:

  1. 變數宣告:

    • var i = 0;:宣告一個名為 i 的變數,並將其初始化為 0
  2. 非同步函數 setTimeout:

    • setTimeout(function() { console.log(i); }, 1000);
      • setTimeout() 是非同步函數,它將指定的函數推遲執行,直到指定的時間間隔(以毫秒為單位)到期。
      • 函數 function() { console.log(i); } 是 setTimeout() 的回呼函數,當延遲時間到期後,它將被執行。
  3. 變數更新:

    • i++;:在 setTimeout() 函數執行之前,將 i 的值增加 1
  4. 輸出:

    • console.log(i);:將 i 的值輸出到瀏覽器控制台。

執行結果:揭曉真相

現在,我們已經了解了程式碼的結構,讓我們來預測一下它的輸出結果。

1
1

乍看之下,這個結果似乎有些反直覺,因為我們預期回呼函數會在 i 的值增加後執行,並且會輸出 2。但是,這正是 setTimeout() 函數的詭異之處。

解析:抽絲剝繭

程式執行順序:

理解結果的關鍵在於了解 JavaScript 的執行機制。JavaScript 是一個非同步語言,這意味著它使用事件循環來處理非同步任務。當 setTimeout() 函數被調用時,它的回呼函數被推遲執行,直到指定的延遲時間到期。同時,主程式碼將繼續執行,執行完所有同步任務。

函數調用順序:

在我們的例子中,在回呼函數執行之前,主程式碼已經執行到最後一行 console.log(i);,並輸出了 i 的值(1)。當回呼函數在 1000 毫秒延遲後執行時,它輸出 i 的值也是 1,因為在回呼函數執行之前,i 的值並沒有被更新。

非同步程式設計的關鍵:事件循環

事件循環是 JavaScript 非同步程式的核心。它是一個事件隊列,瀏覽器用它來管理非同步任務,例如 setTimeout() 函數。當一個非同步任務被觸發時,它會被添加到事件隊列中。事件循環監控隊列,並在適當時機執行任務。

結論:非同步程式設計的威力

透過這個 JavaScript 智力題,我們深入了解了非同步程式設計,並展示了 JavaScript 如何使用事件循環來實現同時執行。理解這些概念對於構建強大且高效的 JavaScript 應用程式至關重要。

常見問題解答

  1. 為什麼回呼函數沒有輸出 2

    • 因為在回呼函數執行之前,i 的值已經被輸出了。JavaScript 的非同步性質導致回呼函數的執行延遲,這時 i 的值已經被更新。
  2. setTimeout() 函數的實際應用是什麼?

    • setTimeout() 函數可用于在指定的時間間隔後執行任務,例如處理動畫、網路請求或其他需要延遲的任務。
  3. 如何確定回呼函數的執行時機?

    • 回呼函數的執行時機取決於指定的延遲時間。在我們的例子中,回呼函數在延遲 1000 毫秒後執行。
  4. 有哪些替代 setTimeout() 函數的選擇?

    • JavaScript 提供了其他非同步函數,例如 setInterval(), requestAnimationFrame()Promises,它們在不同的場景中提供特定的優點。
  5. 什麼是 JavaScript 的事件循環?

    • 事件循環是 JavaScript 中一個管理非同步任務的機制。它是一個事件隊列,用於排隊並在適當的時候執行非同步任務。