自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

圖例詳解那道setTimeout與循環(huán)閉包的經(jīng)典面試題

開(kāi)發(fā) 前端
值得高興的是很多朋友在讀了文章之后確實(shí)對(duì)閉包有了更加深刻的了解,并準(zhǔn)確的給出了幾種寫(xiě)法。但是也有一些基礎(chǔ)稍差的朋友在閱讀了之后,對(duì)于這題的理解仍然感到困惑,因此應(yīng)一些讀者老爺?shù)囊螅璐宋恼聦?zhuān)門(mén)對(duì)setTimeout進(jìn)行一個(gè)相關(guān)的知識(shí)分享,愿大家讀完之后都能夠有新的收獲。

[[208795]]

我在詳細(xì)圖解作用域鏈與閉包一文中的結(jié)尾留下了一個(gè)關(guān)于setTimeout與循環(huán)閉包的思考題。

利用閉包,修改下面的代碼,讓循環(huán)輸出的結(jié)果依次為1, 2, 3, 4, 5

  1. for (var i=1; i<=5; i++) { 
  2.     setTimeout( function timer() { 
  3.         console.log(i); 
  4.     }, i*1000 ); 
  5.  

值得高興的是很多朋友在讀了文章之后確實(shí)對(duì)閉包有了更加深刻的了解,并準(zhǔn)確的給出了幾種寫(xiě)法。一些朋友能夠認(rèn)真的閱讀我的文章并且一個(gè)例子一個(gè)例子的上手練習(xí),這種認(rèn)可對(duì)我而言真的非常感動(dòng)。但是也有一些基礎(chǔ)稍差的朋友在閱讀了之后,對(duì)于這題的理解仍然感到困惑,因此應(yīng)一些讀者老爺?shù)囊?,借此文章?zhuān)門(mén)對(duì)setTimeout進(jìn)行一個(gè)相關(guān)的知識(shí)分享,愿大家讀完之后都能夠有新的收獲。

在最初學(xué)習(xí)setTimeout的時(shí)候,我們很容易知道setTimeout有兩個(gè)參數(shù),***個(gè)參數(shù)為一個(gè)函數(shù),我們通過(guò)該函數(shù)定義將要執(zhí)行的操作。第二個(gè)參數(shù)為一個(gè)時(shí)間毫秒數(shù),表示延遲執(zhí)行的時(shí)間。

  1. setTimeout(function() { 
  2.     console.log('一秒鐘之后我將被打印出來(lái)'
  3. }, 1000)  

 

上例執(zhí)行結(jié)果

可能不少人對(duì)于setTimeout的理解止步于此,但還是有不少人發(fā)現(xiàn)了一些其他的東西,并在評(píng)論里提出了疑問(wèn)。比如上圖中的這個(gè)數(shù)字7,是什么?

每一個(gè)setTimeout在執(zhí)行時(shí),會(huì)返回一個(gè)***ID,上圖中的數(shù)字7,就是這個(gè)***ID。我們?cè)谑褂脮r(shí),常常會(huì)使用一個(gè)變量將這個(gè)***ID保存起來(lái),用以傳入clearTimeout,清除定時(shí)器。

  1. var timer = setTimeout(function() { 
  2.  
  3. console.log('如果不清除我,我將會(huì)一秒之后出現(xiàn)。'); 
  4.  
  5. }, 1000) 
  6.  
  7. clearTimeout(timer); // 清除之后,通過(guò)setTimeout定義的操作并不會(huì)執(zhí)行  

接下來(lái),我們還需要考慮另外一個(gè)重要的問(wèn)題,那就是setTimeout中定義的操作,在什么時(shí)候執(zhí)行?為了引起大家的重視,我們來(lái)看看下面的例子。

  1. var timer = setTimeout(function() { 
  2.  
  3. console.log('setTimeout actions.'); 
  4.  
  5. }, 0); 
  6.  
  7. console.log('other actions.'); 
  8.  
  9. // 思考一下,當(dāng)我將setTimeout的延遲時(shí)間設(shè)置為0時(shí),上面的執(zhí)行順序會(huì)是什么?  

在瀏覽器中的console中運(yùn)行試試看,很容易就能夠知道答案,如果你沒(méi)有猜中答案,那么我這篇文章就值得你點(diǎn)一個(gè)贊了,因?yàn)榻酉聛?lái)我分享的小知識(shí),可能會(huì)在筆試中救你一命。

在對(duì)于執(zhí)行上下文的介紹中,我與大家分享了函數(shù)調(diào)用棧這種特殊數(shù)據(jù)結(jié)構(gòu)的調(diào)用特性。在這里,將會(huì)介紹另外一個(gè)特殊的隊(duì)列結(jié)構(gòu),頁(yè)面中所有由setTimeout定義的操作,都將放在同一個(gè)隊(duì)列中依次執(zhí)行。

我用下圖跟大家展示一下隊(duì)列數(shù)據(jù)結(jié)構(gòu)的特點(diǎn)。

 

隊(duì)列:先進(jìn)先出

而這個(gè)隊(duì)列執(zhí)行的時(shí)間,需要等待到函數(shù)調(diào)用棧清空之后才開(kāi)始執(zhí)行。即所有可執(zhí)行代碼執(zhí)行完畢之后,才會(huì)開(kāi)始執(zhí)行由setTimeout定義的操作。而這些操作進(jìn)入隊(duì)列的順序,則由設(shè)定的延遲時(shí)間來(lái)決定。

因此在上面這個(gè)例子中,即使我們將延遲時(shí)間設(shè)置為0,它定義的操作仍然需要等待所有代碼執(zhí)行完畢之后才開(kāi)始執(zhí)行。這里的延遲時(shí)間,并非相對(duì)于setTimeout執(zhí)行這一刻,而是相對(duì)于其他代碼執(zhí)行完畢這一刻。所以上面的例子執(zhí)行結(jié)果就非常容易理解了。

為了幫助大家理解,再來(lái)一個(gè)結(jié)合變量提升的更加復(fù)雜的例子。如果你能夠正確看出執(zhí)行順序,那么你對(duì)于函數(shù)的執(zhí)行就有了比較正確的認(rèn)識(shí)了,如果還不能,就回過(guò)頭去看看其他幾篇文章。

  1. setTimeout(function() { 
  2.     console.log(a); 
  3. }, 0); 
  4.   
  5. var a = 10; 
  6.   
  7. console.log(b); 
  8. console.log(fn); 
  9.   
  10. var b = 20; 
  11.   
  12. function fn() { 
  13.     setTimeout(function() { 
  14.         console.log('setTImeout 10ms.'); 
  15.     }, 10); 
  16.   
  17. fn.toString = function() { 
  18.     return 30; 
  19.   
  20. console.log(fn); 
  21.   
  22. setTimeout(function() { 
  23.     console.log('setTimeout 20ms.'); 
  24. }, 20); 
  25.   
  26. fn();  

上栗執(zhí)行結(jié)果

OK,關(guān)于setTimeout就暫時(shí)先介紹到這里,我們回過(guò)頭來(lái)看看那個(gè)循環(huán)閉包的思考題。

  1. for (var i=1; i<=5; i++) { 
  2.     setTimeout( function timer() { 
  3.         console.log(i); 
  4.     }, i*1000 ); 
  5.  

如果我們直接這樣寫(xiě),根據(jù)setTimeout定義的操作在函數(shù)調(diào)用棧清空之后才會(huì)執(zhí)行的特點(diǎn),for循環(huán)里定義了5個(gè)setTimeout操作。而當(dāng)這些操作開(kāi)始執(zhí)行時(shí),for循環(huán)的i值,已經(jīng)先一步變成了6。因此輸出結(jié)果總為6。而我們想要讓輸出結(jié)果依次執(zhí)行,我們就必須借助閉包的特性,每次循環(huán)時(shí),將i值保存在一個(gè)閉包中,當(dāng)setTimeout中定義的操作執(zhí)行時(shí),則訪(fǎng)問(wèn)對(duì)應(yīng)閉包保存的i值即可。

而我們知道在函數(shù)中閉包判定的準(zhǔn)則,即執(zhí)行時(shí)是否在內(nèi)部定義的函數(shù)中訪(fǎng)問(wèn)了上層作用域的變量。因此我們需要包裹一層自執(zhí)行函數(shù)為閉包的形成提供條件。

因此,我們只需要2個(gè)操作就可以完成題目需求,一是使用自執(zhí)行函數(shù)提供閉包條件,二是傳入i值并保存在閉包中。

  1. for (var i=1; i<=5; i++) { 
  2.   
  3.     (function(i) { 
  4.         setTimeout( function timer() { 
  5.             console.log(i); 
  6.         }, i*1000 ); 
  7.     })(i) 
  8.  

 

利用斷點(diǎn)調(diào)試,在chrome中查看執(zhí)行順序與每一個(gè)閉包中不同的i值

當(dāng)然,也可以在setTimeout的***個(gè)參數(shù)處利用閉包。

  1. for (var i=1; i<=5; i++) {  
  2.    setTimeout( (function(i) { 
  3.         return function() { 
  4.             console.log(i); 
  5.         } 
  6.     })(i), i*1000 ); 
  7.  
責(zé)任編輯:龐桂玉 來(lái)源: 前端大全
相關(guān)推薦

2024-04-15 08:34:43

2024-04-28 08:23:18

2021-03-01 09:39:34

閉包JavaScript開(kāi)發(fā)

2015-11-25 10:48:44

JS閉包面試題

2021-10-26 11:45:22

Vue面試前端

2011-06-14 09:12:03

JavaScript

2010-11-26 10:53:29

戴爾

2024-04-09 08:32:58

Java面試題線(xiàn)程

2024-06-04 14:52:28

2024-04-07 08:23:49

Java面試題集合框架

2014-07-28 14:00:40

linux面試題

2017-12-14 08:04:21

Java面試程序

2022-06-27 09:14:34

JavaScript閉包代碼

2020-11-13 16:00:57

SpringCloud面試架構(gòu)

2024-10-11 17:09:27

2024-01-01 15:30:59

JavaScriptWeb 應(yīng)用程序開(kāi)發(fā)

2023-11-09 16:13:00

TypeScript前端

2024-02-26 15:35:44

2016-01-11 11:50:39

JavaScript閉包面試題

2023-09-21 14:55:24

Web 開(kāi)發(fā)TypeScript
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)