在使用for循環(huán)的時候,假如需要在循環(huán)體中添加一個匿名函數(shù)處理其他的事情,那么,在這個匿名函數(shù)內(nèi),如果需要用到對應(yīng)的i,因為閉包的緣故,循環(huán)體循環(huán)結(jié)束后才返回i,所以i最終為最后一次++的數(shù)值。
閉包即函數(shù)有權(quán)訪問另一函數(shù)的局部變量,常用方法為在函數(shù)內(nèi)部創(chuàng)建另一個需要引用這個函數(shù)內(nèi)部變量的函數(shù)。
解決方式1
通過匿名函數(shù)傳參,因為匿名函數(shù)取得參數(shù)是每次for循環(huán)里的i,所以每次打印的值為0,1,2,......
匿名函數(shù)自我執(zhí)行的方法是,在函數(shù)體外套一對圓括號,形成一個表達(dá)式,在圓括號后再加另一個圓括號,里面可傳參數(shù)。此方法即IIFE,又叫立即執(zhí)行函數(shù)表達(dá)式。
寫到這里,還需要說一下函數(shù)聲明和函數(shù)表達(dá)式的區(qū)別:
1.函數(shù)聲明必須有標(biāo)識符,即函數(shù)名;
2.函數(shù)聲明存在變量提升;
3.函數(shù)聲明不能出現(xiàn)在循環(huán),判斷、try、with等語句的代碼塊中;
解決方式2
此方法和上述方法有異曲同工之妙,也是在匿名函數(shù)體外部取到了循環(huán)體中的i;
在JS中,每一個函數(shù)被調(diào)用的時候都會創(chuàng)建一個執(zhí)行上下文,在該函數(shù)內(nèi)部定義的變量和函數(shù)只能在該函數(shù)內(nèi)部被使用,正是因為這個上下文,使得我們在調(diào)用函數(shù)的時候能創(chuàng)建一些私有變量。
為什么a()()兩次打印都是1,是因為每次執(zhí)行a()()的時候都給a重新賦值1,而b()/c()執(zhí)行的只是a return出來的匿名函數(shù);
為什么報錯?
因為在javascript解析代碼時,當(dāng)遇到function關(guān)鍵字時,會默認(rèn)把它當(dāng)作一個函數(shù)聲明,而不是函數(shù)表達(dá)式,如果沒有顯示的表達(dá)成函數(shù)表達(dá)式,就報錯。因為函數(shù)聲明需要一個函數(shù)名,而上面的代碼中函數(shù)沒有函數(shù)名。(在執(zhí)行到第一個左括號時報錯)
為什么在加了函數(shù)名之后,依然報錯?
在一個表達(dá)式后面加上括號表示立即執(zhí)行,而在一個語句后加上括號,該括號和之前的語句完全不搭邊,而只是一個分組操作符,用來控制運算中的優(yōu)先級,當(dāng)js解析到括號時,發(fā)現(xiàn)里面為空,所以報錯。(在執(zhí)行到第二個右括號時報錯)
因為在js中括號內(nèi)部不能為語句,所以js解析到括號時,緊接著發(fā)現(xiàn)了function關(guān)鍵字,所以自動把括號內(nèi)的語句當(dāng)作表達(dá)式而不是函數(shù)聲明。
所以,立即執(zhí)行函數(shù),你可以這么寫:
而上面我們用立即執(zhí)行函數(shù)加閉包,取到了循環(huán)體中的i;可見合理利用立即執(zhí)行函數(shù)加上閉包,還能保存變量的狀態(tài)。
在模塊化中,也可以用立即執(zhí)行函數(shù)來處理模塊化,可以減少全局變量造成的空間污染,構(gòu)造更多的私有變量。