javascript
好程序员前端教程之JavaScript闭包和匿名函数的关系详解...
好程序員前端教程之JavaScript閉包和匿名函數(shù)的關(guān)系詳解
本文講的是關(guān)于JavaScript閉包和匿名函數(shù)兩者之間的關(guān)系,從匿名函數(shù)概念到立即執(zhí)行函數(shù),最后到閉包。下面一起來看看文章分析,希望你會(huì)喜歡。
前面講了一篇在for循環(huán)中加setTimeout輸出內(nèi)容,我們用到了一個(gè)閉包,但同時(shí)也可以說是匿名函數(shù),到底匿名函數(shù)和閉包有沒有關(guān)系呢?【答案是它們之間沒有關(guān)系】
匿名函數(shù)
匿名函數(shù),顧名思義,就是沒有名字的函數(shù),與之對(duì)應(yīng)的就是有名字的函數(shù),也叫具名函數(shù)。
//匿名函數(shù)
function (){
}
//具名函數(shù)
function myFn(){
}
//變量a就是匿名函數(shù)的名字
var a = function(){
}
如果我們直接在控制臺(tái)中運(yùn)行匿名函數(shù),會(huì)發(fā)現(xiàn)報(bào)錯(cuò),無法執(zhí)行。匿名函數(shù)是無法執(zhí)行的,一般用到匿名函數(shù)的時(shí)候都是立即執(zhí)行,也叫自執(zhí)行匿名函數(shù)或者自調(diào)用匿名函數(shù),一般人都叫立即執(zhí)行函數(shù)。
立即執(zhí)行函數(shù)
比較常見的立即執(zhí)行函數(shù)如下:
;(function(){
})()
;(function(){
console.log('caibaojian.com');}());
上面這兩種都是典型的立即執(zhí)行函數(shù)寫法,兩者的區(qū)分就是一個(gè)執(zhí)行在匿名函數(shù)括號(hào)外面,另外一個(gè)發(fā)起執(zhí)行的括號(hào)在匿名函數(shù)里面。比較常見的是第一種寫法,括號(hào)在匿名函數(shù)的括號(hào)外面。
步驟分解:
1.首先聲明一個(gè)匿名函數(shù) function(){alert('我是匿名函數(shù)')}。
2.然后在匿名函數(shù)后面接一對(duì)括號(hào) (),調(diào)用這個(gè)匿名函數(shù)。
那為什么還要用一個(gè)括號(hào)包起來呢?其實(shí)是為了兼容JS的語法,如果我們不加括號(hào),直接寫成
function (){alert('我是匿名函數(shù)')}()
瀏覽器會(huì)報(bào)語法錯(cuò)誤,想要通過瀏覽器的語法檢查,必須加點(diǎn)小東西,比如下面幾種
(function(){alert('我是匿名函數(shù)')} ()) // 用括號(hào)把整個(gè)表達(dá)式包起來
(function(){alert('我是匿名函數(shù)')}) () //用括號(hào)把函數(shù)包起來
!function(){alert('我是匿名函數(shù)')}() // 求反,我們不在意值是多少,只想通過語法檢查。
+function(){alert('我是匿名函數(shù)')}()
-function(){alert('我是匿名函數(shù)')}()
~function(){alert('我是匿名函數(shù)')}()
void function(){alert('我是匿名函數(shù)')}()
new function(){alert('我是匿名函數(shù)')}()
實(shí)際上,立即執(zhí)行函數(shù)的作用只有一個(gè):創(chuàng)建一個(gè)獨(dú)立的作用域,在這個(gè)作用域里面,外面訪問不到,避免變量污染。比如我們前面的一篇文章,setTimeout的第三個(gè)參數(shù)里面講到的一道題目。
for(var i=0;i<6;i++){
}
我們發(fā)現(xiàn)上面這個(gè)定時(shí)器總是輸出6,因?yàn)閟etTimeout里面的執(zhí)行函數(shù)是異步的,執(zhí)行的時(shí)候,i的值是貫穿整個(gè)作用域的,而不是單獨(dú)一個(gè)給每個(gè)定期器分配了一個(gè)i,for運(yùn)行完的值是6,此時(shí)輸出就總是6了。
那怎么解決呢?用立即執(zhí)行函數(shù)給每個(gè)定時(shí)器創(chuàng)造一個(gè)獨(dú)立作用域即可。
for(var i=0;i<6;i++){
}
在for循環(huán)執(zhí)行時(shí),立即執(zhí)行函數(shù)就已經(jīng)有了結(jié)果了。而每個(gè)立即執(zhí)行函數(shù)里面的j值就是獨(dú)立的一個(gè),不會(huì)受后面影響。所以會(huì)分別執(zhí)行5次定時(shí)器。
//第一個(gè)立即執(zhí)行函數(shù)
(function(0){
})(0);
//第二個(gè)立即執(zhí)行函數(shù)
(function(1){
})(1);
//……
//第六個(gè)立即執(zhí)行函數(shù)
(function(5){
})(5);
i 的值從 0 變化到 5,對(duì)應(yīng) 6 個(gè)立即執(zhí)行函數(shù),這 6 個(gè)立即執(zhí)行函數(shù)里面的 j 「分別」是 0、1、2、3、4、5。
上面說了這么多關(guān)于匿名函數(shù)和立即執(zhí)行函數(shù)的,相信你對(duì)這兩個(gè)概念已經(jīng)很清楚,那么閉包跟匿名函數(shù)有關(guān)系嗎?
閉包
js閉包是指有權(quán)訪問另一個(gè)函數(shù)作用域中的變量的函數(shù),個(gè)人認(rèn)為js閉包最大的用處就是防止對(duì)全局作用域的污染。閉包最神奇的地方就是能在一個(gè)函數(shù)外訪問函數(shù)中的局部變量,把這些變量用閉包的形式放在函數(shù)中便能避免污染。
我們可以分離出上面的第一個(gè)立即執(zhí)行函數(shù)
function box(i){
}
box(1);
//或者這樣
function box(i){
}
var outer = box(1);
outer();
結(jié)論
很明顯這是一個(gè)閉包,然后我們?cè)倏纯次覀冏钋懊娴哪涿瘮?shù)代碼和立即執(zhí)行函數(shù)代碼,可以看出匿名函數(shù)和閉包兩者并沒有關(guān)系。閉包既可以在匿名函數(shù)也可以在具名函數(shù)中使用。
這個(gè)for循環(huán)中的閉包怎么理解以及自執(zhí)行匿名函數(shù)的作用:
這個(gè)for循環(huán)產(chǎn)生的閉包其實(shí)是定時(shí)器的回調(diào)函數(shù),這些回調(diào)函數(shù)的執(zhí)行環(huán)境是window,類似剛才例子中的引用inner的全局outer的執(zhí)行環(huán)境,匿名函數(shù)則相當(dāng)于剛才例子中的box函數(shù)。
Stackoverflow網(wǎng)站上的一個(gè)提問跟我們今天分析的類似。有一個(gè)回答挺好。
閉包機(jī)制適用于所有JavaScript函數(shù),無論是否匿名。
我認(rèn)為這兩個(gè)概念之間的混淆來自于使用術(shù)語“閉包”,其中作者已經(jīng)說過“下面的代碼創(chuàng)建一個(gè)閉包”,然后給出了一個(gè)恰好使用匿名函數(shù)的例子。 在這種情況下,閉包機(jī)制通常是使特定代碼段按預(yù)期工作的重要因素,而使用匿名函數(shù)而不是命名函數(shù)恰好是編碼它的便捷方式。 閱讀這些例子并且第一次看到“閉包”的人然后誤解了這個(gè)術(shù)語,并繼續(xù)在他們自己的Stack Overflow或博客文章中錯(cuò)誤地使用它,因此混亂傳播。
一開始我以為匿名函數(shù)跟閉包有關(guān)系,那是因?yàn)榍『眠@個(gè)定時(shí)器使用了閉包和匿名函數(shù),讓我們誤認(rèn)為兩者之間有關(guān)系,其實(shí)還有很多種方法可以解決這個(gè)問題,比如我們之前說到的setTimeout的第三個(gè)參數(shù),同樣可以得到跟使用立即執(zhí)行函數(shù)同樣的效果。
所以說匿名函數(shù)和閉包之間沒有什么關(guān)系,只不過很多時(shí)候在用到匿名函數(shù)解決問題的時(shí)候恰好形成了一個(gè)閉包,就導(dǎo)致很多人分不清楚匿名函數(shù)和閉包的關(guān)系。
總結(jié)
以上是生活随笔為你收集整理的好程序员前端教程之JavaScript闭包和匿名函数的关系详解...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 字符串补0操作
- 下一篇: SpringBoot-07:Spring