從延時打印0-5看作用域和閉包

《你不知道的JavaScript上卷》第一部分第5章

1.第一次延時打印

for (var i = 0; i <= 5; i++) {
    setTimeout(() => {
        console.log(i)
    }, i * 1000);
}
console.log("global i=", i)
//結(jié)果:先打印global i= 6披泪,然后延時打印6個6

結(jié)果分析

  1. 終止循環(huán)的條件是i<=5,第一次滿足條件的i值是6,所以循環(huán)結(jié)束的時候i的值是6;
  2. setTimeout()是一個宏任務(wù)态贤,需等待上一個宏任務(wù)完成才會執(zhí)行端辱,所以會先打印global i= 6;
  3. 在for循環(huán)內(nèi)的語句var i = 0;var i聲明會被提升到全局脯颜,所以全局只有一個i哟旗;
  4. 在for循環(huán)內(nèi)創(chuàng)建了6個setTimeout,第二個參數(shù)分別是0栋操,1000闸餐,2000 ... 5000
  5. 當(dāng)外部的宏任務(wù)執(zhí)行完畢之后,開始執(zhí)行宏任務(wù)setTimeout()矾芙,然后開始打印i的值舍沙,此時i=6;
  6. 第一個setTimeout的延時參數(shù)值是0,所以會再打印global i= 6之后立即打印一個6剔宪,隨后每隔一秒打印一個6拂铡;

2.第二次延時打印

每次循環(huán)都創(chuàng)建新的自執(zhí)行函數(shù)

for (var i = 0; i <= 5; i++) {
    (
        function () {
            var j = i;
            setTimeout(() => {
                console.log(j)
            }, j * 1000);
        }
    )();
}
console.log("global i=", i);
//結(jié)果:global i= 6 0 1 2 3 4 5 延時打印0-5;

實現(xiàn)所要的效果葱绒!
簡化感帅,將i作為參數(shù)傳遞進(jìn)去

for (var i = 0; i <= 5; i++) {
    (
        function (j) {
            setTimeout(() => {
                console.log(j)
            }, j * 1000);
        }
    )(i);
}
console.log("global i=", i);
//結(jié)果:global i= 6 0 1 2 3 4 5 延時打印0-5;

3.第三次延時打印

使用let聲明變量地淀,實現(xiàn)效果

for (var i = 0; i <= 5; i++) {
    let j = i;
    setTimeout(() => {
        console.log(j)
    }, j * 1000);
}
console.log("global i=", i);
//結(jié)果:global i= 6 0 1 2 3 4 5 延時打印0-5失球;
for (var i = 0; i <= 5; i++) {
    var j = i;
    setTimeout(() => {
        console.log(j)
    }, j * 1000);
}
console.log("global i=", i);
console.log("global j=", j);
//結(jié)果:先打印global i= 6,global j= 5骚秦,然后延時打印6個5她倘,重點是5

問題:let j = i;做了什么,為什么可以實現(xiàn)效果作箍?換成var j = i;為什么不行硬梁?

  1. let關(guān)鍵字可以將變量綁定到所在的任意作用域中,換句話說胞得,let為其聲明的變量隱式的劫持了所在的塊作用域荧止;
  2. 每次循環(huán)都會創(chuàng)建一個新的j,setTimeout執(zhí)行時最終打印的是屬于它那次循環(huán)的j阶剑;
  3. 在JavaScript中存在四個語法作用域:表達(dá)式跃巡,語句,函數(shù)和全局牧愁,在變量的作用域上素邪,并沒有"語句"這個級別,只有全局作用域和函數(shù)作用域兩種猪半,所以var j = i兔朦;還是會創(chuàng)建全局的變量j偷线;

語法作用域存在4種等級

  1. 相同級別的語法作用域可以互相嵌套 例如:if嵌套for,function互相嵌套
  2. 高級別的語法作用域可以嵌套低級別的語法作用域 例如:function嵌套for或者if
  3. 低級別的語法作用域不能嵌套高級別的語法作用域 例如:if嵌套function

if與for的語法作用域是同級別沽甥,所以效果和for一樣

if(true){
    var i = 0;
}
console.log("global i=",i)
var j = 1;
if(true){
    j = 2;
}
console.log("global j=",j)
//結(jié)果:global i= 0 global j= 2

4.第四次延時打印

for(let i = 0;i<=5;i++){
    setTimeout(() => {
        console.log(i)
    }, i * 1000);
}
console.log("global i=", i);
//結(jié)果:報錯声邦,ReferenceError: i is not defined 也就是說i并未在全局聲明
for(let i = 0;i<=5;i++){
    setTimeout(() => {
        console.log(i)
    }, i * 1000);
}
//結(jié)果:延時順序打印 0 1 2 3 4 5

5. 閉包在哪里?

函數(shù)對象可以通過作用域鏈相互關(guān)聯(lián)起來摆舟,函數(shù)體內(nèi)部的變量都可以保存在函數(shù)作用域內(nèi)亥曹,這種特性被稱為“閉包“ --《JavaScript權(quán)威指南》

setTimeout的第一個參數(shù)是一個函數(shù),當(dāng)使用let進(jìn)行變量聲明的時候恨诱,它記住了每次循環(huán)聲明的變量i媳瞪,并且在延時結(jié)束后能夠訪問到i并把i的值打印出來,這就是閉包胡野。

總結(jié)

  1. for循環(huán)內(nèi)聲明的變量會被提升到它所在的作用域材失;
  2. let聲明變量會將變量綁定到所在作用域中;
  3. setTimeout內(nèi)部函數(shù)能記住并訪問所在其所在作用域內(nèi)的變量硫豆,這種特性被稱為閉包龙巨;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市熊响,隨后出現(xiàn)的幾起案子旨别,更是在濱河造成了極大的恐慌,老刑警劉巖汗茄,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件秸弛,死亡現(xiàn)場離奇詭異,居然都是意外死亡洪碳,警方通過查閱死者的電腦和手機(jī)递览,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瞳腌,“玉大人绞铃,你說我怎么就攤上這事∩┦蹋” “怎么了儿捧?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長挑宠。 經(jīng)常有香客問我菲盾,道長,這世上最難降的妖魔是什么各淀? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任懒鉴,我火速辦了婚禮,結(jié)果婚禮上碎浇,老公的妹妹穿的比我還像新娘临谱。我一直安慰自己咆畏,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布吴裤。 她就那樣靜靜地躺著,像睡著了一般溺健。 火紅的嫁衣襯著肌膚如雪麦牺。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天鞭缭,我揣著相機(jī)與錄音剖膳,去河邊找鬼。 笑死岭辣,一個胖子當(dāng)著我的面吹牛吱晒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播沦童,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼仑濒,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了偷遗?” 一聲冷哼從身側(cè)響起墩瞳,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎氏豌,沒想到半個月后喉酌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡泵喘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年泪电,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片纪铺。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡相速,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出霹陡,到底是詐尸還是另有隱情和蚪,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布烹棉,位于F島的核電站攒霹,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏浆洗。R本人自食惡果不足惜催束,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望伏社。 院中可真熱鬧抠刺,春花似錦塔淤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至罕容,卻和暖如春备恤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背锦秒。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工露泊, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人旅择。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓惭笑,卻偏偏與公主長得像,于是被迫代替她去往敵國和親生真。 傳聞我的和親對象是個殘疾皇子沉噩,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

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

  • 官方中文版原文鏈接 感謝社區(qū)中各位的大力支持,譯者再次奉上一點點福利:阿里云產(chǎn)品券汇歹,享受所有官網(wǎng)優(yōu)惠屁擅,并抽取幸運大...
    HetfieldJoe閱讀 5,597評論 16 88
  • 你不懂JS:作用域與閉包 第五章:作用域閉包 希望我們是帶著對作用域工作方式的健全,堅實的理解來到這里的产弹。 我們將...
    寒彧兒閱讀 118評論 0 1
  • [圖片上傳失敗...(image-9ad041-1561218142988)] 目錄 概述 作用域編譯過程詞法作用...
    M1mmm閱讀 269評論 0 1
  • 目錄 概述 作用域編譯過程詞法作用域全局作用域函數(shù)作用域 閉包循環(huán)和閉包閉包的用途性能 總結(jié) 概述 作用域和閉包一...
    許驍Charles閱讀 470評論 0 1
  • 特別說明派歌,為便于查閱,文章轉(zhuǎn)自https://github.com/getify/You-Dont-Know-JS...
    殺破狼real閱讀 480評論 0 0