前面的閉包中,提到與閉包相似的立即執(zhí)行函數(shù)粗截,感覺兩者還是比較容易弄混吧,嚴(yán)格來(lái)說(shuō)(因?yàn)橄透叱虒?duì)閉包的定義不同)捣炬,立即執(zhí)行函數(shù)并不屬于閉包熊昌,它不滿足閉包的三個(gè)條件。
一湿酸、圓括號(hào)運(yùn)算符
圓括號(hào)運(yùn)算符也叫分組運(yùn)算符婿屹,它有兩種用法:如果表達(dá)式放在圓括號(hào)中,作用是求值推溃;如果跟在函數(shù)后面昂利,作用是調(diào)用函數(shù)
把表達(dá)式放在圓括號(hào)之中,將返回表達(dá)式的值
console.log((1+2)); // 3
將函數(shù)放在圓括號(hào)中铁坎,會(huì)返回函數(shù)本身蜂奸。如果圓括號(hào)緊跟在函數(shù)的后面,就表示調(diào)用函數(shù)硬萍,即對(duì)函數(shù)求值
console.log((function testa(){return 666;}));
// function testa(){return 666;}
console.log(function testa(){return 666;}());
// 666
注意:圓括號(hào)運(yùn)算符不能為空扩所,否則會(huì)報(bào)錯(cuò)
();//SyntaxError: Unexpected token )
由于圓括號(hào)的作用是求值,如果將語(yǔ)句放在圓括號(hào)之中襟铭,就會(huì)報(bào)錯(cuò)碌奉,因?yàn)檎Z(yǔ)句沒有返回值
(var a = function(){return 666});
// SyntaxError: Unexpected token var
二短曾、函數(shù)聲明
使用 function
關(guān)鍵字創(chuàng)建一個(gè)函數(shù),并且后面帶有函數(shù)名赐劣,叫函數(shù)聲明嫉拐。
function testa(){}
三、匿名函數(shù)
那么使用 function
關(guān)鍵字創(chuàng)建的函數(shù)不帶函數(shù)名呢魁兼? 那就是匿名函數(shù)了婉徘。
function (){}
四、函數(shù)表達(dá)式
那么把匿名函數(shù)賦值給一個(gè)變量呢咐汞?那就是函數(shù)表達(dá)式了盖呼。
var testa = function (){}
其實(shí)呢,函數(shù)表達(dá)式的根本所在化撕,就是阻止了js引擎把 用function
創(chuàng)建的函數(shù) 當(dāng)作函數(shù)聲明來(lái)解析几晤。下面再詳說(shuō)。
五植阴、立即執(zhí)行函數(shù)(IIFE)
那么立即執(zhí)行函數(shù)呢蟹瘾?
用function
定義函數(shù)之后,立即調(diào)用該函數(shù)掠手。這種函數(shù)就叫做立即執(zhí)行函數(shù)憾朴,全稱為立即調(diào)用的函數(shù)表達(dá)式IIFE(Imdiately Invoked Function Expression)
1、在本系列進(jìn)擊的 JavaScript(三)中到過(guò)喷鸽,代碼執(zhí)行時(shí)众雷,會(huì)先對(duì)函數(shù)聲明的函數(shù) 進(jìn)行解析(函數(shù)聲明提升),而函數(shù)表達(dá)式做祝,當(dāng)逐行執(zhí)行到它時(shí)砾省,才會(huì)解析。
2剖淀、正因?yàn)楹瘮?shù)聲明的提升纯蛾,導(dǎo)致函數(shù)聲明不能立即執(zhí)行纤房。因?yàn)樽莞簦瘮?shù)聲明時(shí),js只會(huì)解析到大括號(hào)(})就結(jié)束了炮姨,如果后面有()
捌刮,只是一個(gè)圓括號(hào)運(yùn)算符。
function testa(){
console.log("testa")
}("666")
//"666"
//如果后面是一個(gè)空的圓括號(hào)舒岸,會(huì)報(bào)錯(cuò)绅作,上面提到過(guò)鸭津。
所以摔刁,不知道,你有沒有發(fā)現(xiàn)蒙揣,函數(shù)聲明的函數(shù),后面可以不用分號(hào)(;)分隔眯杏,也可以正常執(zhí)行夜焦,而函數(shù)表達(dá)式的后面就必須加分號(hào),不然會(huì)報(bào)錯(cuò)岂贩。你可以自己寫個(gè)小栗子驗(yàn)證下茫经。
3、匿名函數(shù)是不能單獨(dú)寫的萎津,所以就提不上立即執(zhí)行了卸伞。
function (){}
//Uncaught SyntaxError: Unexpected token (
單獨(dú)寫匿名函數(shù),是會(huì)報(bào)錯(cuò)的锉屈,js引擎 會(huì)把它當(dāng)作函數(shù)聲明來(lái)解析荤傲,而函數(shù)聲明就必須要有個(gè)函數(shù)名,所以會(huì)報(bào)錯(cuò)颈渊。
所以弃酌,你通常看到使用匿名函數(shù)儡炼,都是當(dāng)作參數(shù)傳遞的妓湘,或者把匿名函數(shù)轉(zhuǎn)為函數(shù)表達(dá)式。
4乌询、因此榜贴,只有函數(shù)表達(dá)式可以立即執(zhí)行
var testa = function (){
console.log("testa")
}()
//"testa"
上面提到過(guò),函數(shù)表達(dá)式妹田,就是阻止了js引擎把 |用function
創(chuàng)建的函數(shù)| 當(dāng)作函數(shù)聲明來(lái)解析唬党。
注:javascript引擎規(guī)定,如果function關(guān)鍵字出現(xiàn)在行首鬼佣,一律解釋成函數(shù)聲明語(yǔ)句驶拱。
所以,解決方法就是不要讓function出現(xiàn)在行首晶衷,讓引擎將其理解成一個(gè)表達(dá)式蓝纲。
//常用的兩種,使用圓括號(hào)運(yùn)算符
(function(){console.log("666")})()
(function(){console.log("666")}())
//一元運(yùn)算符寫法
!function(){console.log("666")}()
+function(){console.log("666")}()
-function(){console.log("666")}()
~function(){console.log("666")}()
都是都可以把函數(shù)聲明 轉(zhuǎn)為 函數(shù)表達(dá)式,也就是阻止了把其當(dāng)作函數(shù)聲明解析晌纫。
六税迷、立即執(zhí)行函數(shù)在閉包中的應(yīng)用
1、立即執(zhí)行函數(shù)能配合閉包保存狀態(tài)锹漱。
來(lái)看下 上節(jié)內(nèi)容中閉包的例子:
function makeClosures(i){
var i = i;
return function(){
console.log(i);
}
}
for (var i=1; i<=5; i++) {
setTimeout(makeClosures(i),i*1000);
}
//1
//2
//3
//4
//5
現(xiàn)在箭养,我們來(lái)利用立即執(zhí)行函數(shù)來(lái)簡(jiǎn)化它:
for (var i=1; i<=5; i++) {
setTimeout((function(i){
return function(){
console.log(i);
}
})(i),i*1000);
}
第一個(gè)匿名函數(shù)執(zhí)行完畢后,返回了第二個(gè)匿名函數(shù)哥牍。第二個(gè)匿名函數(shù)被當(dāng)做setTimeout 的第一個(gè)參數(shù)傳入進(jìn)去毕泌。因?yàn)?setTimeout函數(shù)執(zhí)行了5次喝检,所以立即執(zhí)行函數(shù)里每次都會(huì)返回了一個(gè)沒有被執(zhí)行的匿名函數(shù),(這里就是返回了5個(gè)匿名函數(shù))撼泛,每個(gè)匿名函數(shù)內(nèi)部保存著每次傳進(jìn)來(lái)的i值蛇耀,因此,每個(gè)i 都是不一樣的坎弯,所以纺涤,就得到了想要的結(jié)果
2、立即執(zhí)行函數(shù)配合閉包 模塊化中應(yīng)用
(function(){
var meg = "hello zdx";
function say(arg){
arg = arg || meg;
console.log(arg)
}
window.say = say;
})(window)
window.say();
//"hello zdx"
首先立即執(zhí)行函數(shù)抠忘,它是個(gè)匿名函數(shù)撩炊,你是得不到它的函數(shù)引用,這樣崎脉,就避免了全局變量污染拧咳。其次,由于函數(shù)作用域的規(guī)則囚灼,在匿名函數(shù)外部是訪問(wèn)不了函數(shù)內(nèi)的變量骆膝,函數(shù)等的。所以灶体,也經(jīng)常用立即執(zhí)行函數(shù)模擬塊級(jí)作用域阅签。