天天看點

JS網紅面試題-setTimeout與循環閉包

先看一段代碼

for (var i = 0; i < 5; i++) {
    setTimeout(() => {
        console.log(i);
    }, i * 1000);
}
           

輸出結果是什麼?? 肯定有好多人以為是 0 1 2 3 4

這麼想當然是錯的,因為setTimeout()執行的是一個異步的操作,那能異步到什麼地步呢,就是當所有的代碼執行完畢之後,才會執行setTimeout() 是以當setTimeout函數執行的時候for循環早就已經執行完畢了,那麼此時的i值就是5 是以正确的結果就是 5 5 5 5 5

  • 現在來思考一個問題,假如說我就是想用這種方式來實作輸出0 1 2 3 4呢??有可能嗎?怎麼實作呢?
  • 不賣關子了,明确的說使用閉包的原理可以解決這個問題。

閉包在JS中被稱為神話一樣的存在

  • 要想形成閉包一個必要的條件,你得有一個父級的函數,包包覆延時器,當延時器在被調用的時候,延時器與foo内部的上下文形成閉包,類似于下面這樣
for (var i = 0; i < 5; i++) {
    function foo() { //父級函數
        setTimeout(() => {
            console.log(i);
        }, i * 1000);
    }
    foo()
}
           
  • 如上父級函數foo包包覆了延時器setTimeout,但是現在我們運作仿佛結果還是 5 5 5 5 5 怎麼回事哪裡出錯了嗎???
  • 究其原因原來是因為,setTimeout在調用的時候雖然與foo()的内部形成了閉包,但是我們并沒有把i的值給限定在每一個閉包裡面,于是改成下面的代碼就可以如願以償了
for (var i = 0; i < 5; i++) {
    function foo(i) { //父級函數
        setTimeout(() => {
            console.log(i);
        }, i * 1000);
    }
    foo(i)
}