前端基礎(chǔ)進(jìn)階(六):setTimeout與循環(huán)閉包面試題詳解

配圖與原文無(wú)關(guān)

我在閉包一文中的結(jié)尾留下了一個(gè)關(guān)于setTimeout與循環(huán)閉包的思考題谷饿。

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

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

值得高興的是听怕,很多朋友在閱讀了我的文章之后確實(shí)對(duì)閉包有了更加深刻的了解,并準(zhǔn)確的給出了好幾種寫(xiě)法虑绵。大家能夠認(rèn)真的閱讀我的文章并且一個(gè)例子一個(gè)例子的上手練習(xí)尿瞭,這種認(rèn)可對(duì)我而言真的非常感動(dòng)。

但是也有一些基礎(chǔ)稍差的朋友在閱讀了之后翅睛,對(duì)于這題的理解仍然感到困惑声搁,因此應(yīng)一些讀者老爺?shù)囊螅璐宋恼聦iT(mén)對(duì)setTimeout進(jìn)行一個(gè)相關(guān)的知識(shí)分享捕发,希望大家讀完之后都能夠有新的收獲疏旨。

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

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

執(zhí)行結(jié)果如圖

image

可能不少同學(xué)對(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í)器。

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

var timer = setTimeout(function() {
  console.log('setTimeout actions.');
}, 0);
 
console.log('other actions.');

思考一下冲呢,當(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)进鸠。

image

隊(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)決定。

更加詳細(xì)的執(zhí)行順序司恳,將會(huì)在事件循環(huán)的文中中描述

因此在上面這個(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ò)頭去看看其他幾篇文章倘屹。

setTimeout(function () {
  console.log(a);
}, 0);

var a = 10;

console.log(b);
console.log(fn);

var b = 20;

function fn() {
  setTimeout(function () {
    console.log('setTImeout 10ms.');
  }, 10);
}

fn.toString = function () {
  return 30;
}

console.log(fn);

setTimeout(function () {
  console.log('setTimeout 20ms.');
}, 20);

fn();

執(zhí)行結(jié)果如圖所示。


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

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

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

如果我們直接這樣寫(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í),則訪問(wèn)對(duì)應(yīng)閉包保存的i值即可梯影。

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

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

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

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

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

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

下一篇:前端基礎(chǔ)進(jìn)階(七):this
上一篇:前端基礎(chǔ)進(jìn)階(五):閉包
前端基礎(chǔ)進(jìn)階目錄

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末膳灶,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子立由,更是在濱河造成了極大的恐慌轧钓,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锐膜,死亡現(xiàn)場(chǎng)離奇詭異毕箍,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)枣耀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)霉晕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人捞奕,你說(shuō)我怎么就攤上這事≈羟幔” “怎么了颅围?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)恨搓。 經(jīng)常有香客問(wèn)我院促,道長(zhǎng),這世上最難降的妖魔是什么斧抱? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任常拓,我火速辦了婚禮,結(jié)果婚禮上辉浦,老公的妹妹穿的比我還像新娘弄抬。我一直安慰自己,他們只是感情好宪郊,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布掂恕。 她就那樣靜靜地躺著,像睡著了一般弛槐。 火紅的嫁衣襯著肌膚如雪懊亡。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天乎串,我揣著相機(jī)與錄音店枣,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛鸯两,可吹牛的內(nèi)容都是我干的闷旧。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼甩卓,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼鸠匀!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起逾柿,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤缀棍,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后机错,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體爬范,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年弱匪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了青瀑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡萧诫,死狀恐怖斥难,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情帘饶,我是刑警寧澤哑诊,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站及刻,受9級(jí)特大地震影響镀裤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜缴饭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一暑劝、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧颗搂,春花似錦担猛、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至卖丸,卻和暖如春纺且,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背稍浆。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工载碌, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留猜嘱,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓嫁艇,卻偏偏與公主長(zhǎng)得像朗伶,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子步咪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容