天天看點

Javascript的setTimeout()使用閉包特性時需要注意的問題

這是一個正常的函數

并不會按照我們預想的每隔1秒分别輸出0、1、2、3、4

原因:此函數在for循環的第一層是settimeout函數,他的執行和createfunction1中的s函數一樣,将按分别在1秒後、2秒後、3秒後執行。但這兒需要注意的是,settimeout的内部函數timer并沒有立即執行,for循環中的i将會把值分别賦給settimeout外部參數中的i,但其内部函數timer()則隻會引用包含函數settimeout()中的變量的最後一個值。因為閉包所儲存的是整個變量對象,而不是某個特殊的變量。當然其中的這些處理變化,都是瞬間完成的,與執行時間并無關系,即使把1000改成0效果還是一樣的。

重寫一下這個函數:

這個例子,給外部包裝了一個立即執行的匿名函數,settimeout裡面的匿名函數不再引用外部函數的參數,而是直接引用外部匿名函數的參數,這時,一切就會按照我們預想的來執行了

另外es6為我們提供了另一種解決方案:

這兩種方法都不是解決異步問題的,而是解決變量作用域的問題的。因為函數 timer() 屬于一個新的域,通過 var 定義的變量是無法傳入到這個函數執行域中的,于是第一種是通過傳入參數,間接的把變量傳入到 timer 中;第二種是通過使用 let 來聲明塊變量,這時候變量就能作用于這個塊,是以 timer 就能使用 i 這個變量了

有時為了進行異步處理,而使用settimeout(function…,0);

在settimeout設定的函數處理器之前,函數f傳回;

在使用異步處理時,尤其是使用閉包特性時,要特别小心

對于初次使用這種方式的同學來說,很可能會認為程式會列印0…9,可結果确實列印10個10;問題就在于,當循環完成時,function得到執行,而i已經變成10,console.log(i)中使用的是10!

那麼可以換一種方式,用函數參數來儲存0….9(其實也是利用了閉包):

另外再來看一個例子:

把他修改一下:

來看一道考察javascript運作機制的題目

這道題考察javascript的運作機制。首先先碰到一個 settimeout,于是會先設定一個定時,在定時結束後将傳遞這個函數放到任務隊列裡面,是以開始肯定不會輸出 1 。然後是一個 promise,裡面的函數是直接執行的,是以應該直接輸出 2 3 。然後,promise 的 then 應當會放到目前 tick 的最後,但是還是在目前 tick 中。是以,應當先輸出 5,然後再輸出 4 。最後在到下一個 tick,就是 1

繼續閱讀