for循環中嵌套setTimeout,執行順序和結果該如何理解?


這兩天在搗鼓作用域的問題,有的時候知識這個東西真的有點像是牽一發而動全身的感覺。在理解作用域的時候,又看到了一道經典的面試題和例子題。

那就是在for循環中嵌套setTimeout延時,想想之前面試的時候面試官問到我這個問題,然而我當時對這玩意兒根本沒有深究,沒有去理解;非常草率的回答了,面試官好心的給我

說這個涉及到setTimeout回調函數異步特性,啪啦啪啦,說的好幾句都感覺暈乎了~也感覺JS這個東西真的需要深入的東西還很多,路也還很長。

直接進入主題了,先貼一段簡單的代碼:

結果是10次10。不得不說看到答案的第一眼真的魔怔啊,完全不理解為什么是10次10;只有你多去了解JS的執行機制之后,或許心中就有答案了。所以以下是我個人在看了眾多的

網上的各位高手的見解之后,總結以下得到的一個見解。如有歧義,歡迎各位的指正。

     首先這樣的結果需要從JS的執行機制說起。JS是單線程環境,也就是說代碼的執行是從上到下,依次執行。這樣的執行稱為同步執行。因為種種不要浪費和節約的原因。JS中引進了異步的機制。在這段代碼中,哪個是同步哪個是異步呢?for循環是同步代碼,而setTimeout中的是異步代碼。那么JS碰到這個有同步和異步的情況下會先從上到下執行同步代碼,碰到異步的代碼會將其插入到任務隊列當中等待。而setTimeout是延時,也就是說碰到setTimeout這個異步的代碼塊會根據它里面的第二個參數:延時時間來將代碼插入到任務隊列當中,比如上面這段代碼中,第二個參數延時時間是0,也就是說執行到它的時候會在0ms之后將它插入到任務隊列當中。同步代碼都執行完成之后,那么JS引擎就空閑了,這個時候就輪到任務隊列中的異步代碼依次加載了。

這是上面這段代碼的答案的一半。另一半就來自於作用域,作用域是變量等資源的作用范圍。在這段代碼中准確的說是作用域鏈的問題,當同步代碼執行完畢開始執行異步的setTimeout代碼時,setTimeout中需要一個變量 ---i---,而執行的時候在當前的作用域中開始找,找不到變量i的定義,這個時候就把創建這個函數的作用域作為當前作用域,再次尋找,創建這個函數的作用域就是全局作用域,也就是找到了for循環中i,找到了之后就結束尋找變量i的行程。由於這個時候的i是全局的,而且人家已經變為了最終形態:10,setTimeout找到的就是這個i=10;所以就輸出了10,下面的9次setTimeout 的執行都是類似,所以結果都是10;

所以我對這個答案的理解歸結起來就是  異步加載+作用域鏈。可見要理解一段看似簡單的代碼,要去學習的東西還真不少。

問題出來了,相應的就有解決方案;對於這類問題比較常見的解決方法就是 立即執行函數。它逼迫js每次循環進來的時候都會立即去執行代碼,從而保證了每一次得到了i的副本都是不一樣的。貼代碼:

如上圖,得到了想要的答案


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM