淺談 JavaScript 閉包

相信學(xué)習(xí) JavaScript 的同學(xué)都知道「閉包(Closure)」,這個(gè)概念在 JavaScript 中是非常重要的浩淘,并且在大多數(shù)人看來閉包是非常難以理解的概念。既然這樣尤揣,那今天就帶大家一起來看看這到底是何方神圣宙橱。

維基百科是這樣解釋的:

閉包姨俩,又稱詞法閉包或函數(shù)閉包,是引用了自由變量的函數(shù)师郑,這個(gè)被引用的自由變量將和這個(gè)函數(shù)一同存在,即使離開了創(chuàng)造它的環(huán)境也不例外调窍,所以宝冕,閉包是由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實(shí)體

說了半天一句也沒看懂邓萨,那我們來看看 JS 官方是怎么解釋的:

閉包是指多個(gè)變量和綁定了這些變量的環(huán)境的表達(dá)式( 通常是一個(gè)函數(shù) )地梨,因而這些變量也是該表達(dá)式的一部分。

我去缔恳,這又是什么東西宝剖?只看出閉包是個(gè)函數(shù),其他的還是一概不知歉甚。

既然這樣不知道它們?cè)谡f些什么万细,不如跟著我的思路來看一看到底是個(gè)什么。

要理解閉包纸泄,首先我們要弄明白什么是詞法作用域作用域鏈赖钞。

作用域一般有兩種常見的模型,一種叫做詞法作用域聘裁,另一種叫做動(dòng)態(tài)作用域雪营。我們的 JavaScript 就是基于詞法作用域的語言。

簡(jiǎn)單來講衡便,詞法作用域就是一個(gè)變量的作用在出生(定義)時(shí)就已經(jīng)被設(shè)定好了献起,當(dāng)在本作用域中找不到變量時(shí),就會(huì)一直向父作用域中去查找镣陕,直到直到為止谴餐。如果不明白的話,看下面的代碼大概就能理解了茁彭。

代碼中 fun1 在其內(nèi)部已經(jīng)定義了變量 y总寒,所以在查找 y 時(shí)在該作用域(內(nèi)部函數(shù) fun1 中)內(nèi)可以找到,則無需再往父作用域中去查找理肺;如果在其作用域內(nèi)沒有查找到摄闸,則會(huì)在父作用域內(nèi)查找善镰,也就是使用 fun 函數(shù)中的變量 y。

既然 JavaScript 中的函數(shù)和變量都有其作用域年枕,那么作用域之間就會(huì)產(chǎn)生一條鏈炫欺,我們稱之為作用域鏈。假設(shè)我們編寫了一段 JS 代碼熏兄,那這段代碼就會(huì)有一個(gè)與之關(guān)聯(lián)的作用域鏈品洛。這個(gè)作用域鏈就是由全局對(duì)象(如:window)、我們自定義的對(duì)象(函數(shù)摩桶,局部變量)組成桥状。比如上面的代碼,其作用域鏈上是這樣的:函數(shù) fun1硝清、變量 y ==> 函數(shù) fun辅斟、變量 x、y ==> 全局對(duì)象芦拿。這就是所謂的作用域鏈士飒。

理解了上面的內(nèi)容,就可以來看看咱們今天的主人公「閉包」了蔗崎。

函數(shù)對(duì)象可以通過作用域鏈相互關(guān)聯(lián)起來酵幕,函數(shù)體內(nèi)部的變量都可以保存在函數(shù)作用域內(nèi),也就是函數(shù)變量可以被藏在作用域鏈之內(nèi)缓苛,這種特性在計(jì)算機(jī)科學(xué)文獻(xiàn)中稱為閉包芳撒。看上去變量被“封閉包裹”了起來他嫡。由此可見番官,從理論上講,所有的 JavaScript 函數(shù)全都是閉包的钢属,因?yàn)樗鼈兌际菍?duì)象徘熔,它們都關(guān)聯(lián)在作用域鏈上。

那么怎么才能顯式的形成閉包呢淆党?先來看下面的例子酷师。

注意這段代碼中標(biāo)記的地方:內(nèi)部函數(shù) fun1 在執(zhí)行前通過外部函數(shù)被返回了,外部函數(shù)被賦值給了變量 result染乌。這時(shí)山孔,變量 result 的值就變成了函數(shù) fun1,也就是說內(nèi)部變量 name 在所屬函數(shù)外部被調(diào)用了荷憋。我們來證實(shí)一下:

可以看到 result 的值就是函數(shù) fun1台颠,那為什么還可以讀取變量 name 呢?答案就是 result 變成閉包了。

result 由兩部分組成:函數(shù)以及創(chuàng)建該函數(shù)的環(huán)境串前。函數(shù)就是被外部函數(shù)返回的內(nèi)部函數(shù)瘫里,而環(huán)境就是由閉包創(chuàng)建時(shí)在作用域中的任何局部變量組成的。在我們的例子中荡碾,result 是一個(gè)閉包谨读,由函數(shù) fun1 和閉包創(chuàng)建時(shí)存在的「"Google"」字符串形成。

現(xiàn)在想想坛吁,維基百科說的好像就是這么回事:閉包是由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實(shí)體劳殖。這就解釋了為什么可以讀取變量 name 了,因?yàn)?result 引用的環(huán)境是 fun1 函數(shù)相關(guān)的引用環(huán)境拨脉,可以理解為: result 處在 fun1 所處的作用域鏈的位置哆姻,既然這樣,那自然可以讀取變量 name 了女坑。

這就是閉包填具,現(xiàn)在看來也就是這么回事么,沒什么難理解的匆骗。

既然已經(jīng)理解了,那我們?cè)賮砜匆粋€(gè)例子(引用自廖雪峰老師的 JS 教程):

這個(gè)例子中誉简,每次循環(huán)碉就,都創(chuàng)建了一個(gè)新的函數(shù),然后闷串,把創(chuàng)建的 3 個(gè)函數(shù)都添加到數(shù)組 arr 中返回了瓮钥。

那么調(diào)用 f1() 、f2()烹吵、f3() 的結(jié)果是什么呢碉熄?不就是 1,4肋拔,9 嗎? 不是锈津。

你沒看錯(cuò),答案就是 16凉蜂,全部都是琼梆!原因在于閉包 results 返回的數(shù)組中的函數(shù)引用了變量 i,但這個(gè)返回的數(shù)組中的函數(shù)并不是立刻執(zhí)行的窿吩,等到執(zhí)行時(shí)茎杂,它們所引用的變量 i 已經(jīng)變成 4 了,所以結(jié)果為 16纫雁。還是沒明白煌往?上面我們說了,閉包是由函數(shù)和其相關(guān)的引用環(huán)境組合而成的轧邪,既然所處的環(huán)境還是在作用域鏈原來的位置刽脖,那么變量 i 就會(huì)在 for 循環(huán)的作用下變成 4羞海,而到了你去調(diào)用閉包的時(shí)候,閉包引用的變量 i 的值自然為 4 了曾棕,所以結(jié)果自然就是 16 了扣猫。

這個(gè)例子要提醒大家的是:返回的函數(shù),不要引用任何循環(huán)變量和變量值后續(xù)會(huì)發(fā)生變化的變量翘地。這一點(diǎn)在使用閉包時(shí)一定要牢記申尤。

還要說明的一點(diǎn)就是,避免濫用閉包衙耕。原因:使用閉包之后昧穿,閉包中函數(shù)所處的環(huán)境會(huì)一直存在,所以閉包會(huì)使得函數(shù)中的變量都被保存在內(nèi)存中橙喘,不會(huì)被“垃圾回收機(jī)制”回收时鸵,進(jìn)而內(nèi)存消耗過大,造成網(wǎng)頁性能下降厅瞎。

最后饰潜,理解清楚作用域鏈的概念不但對(duì)掌握閉包非常重要,并且對(duì)其他知識(shí)點(diǎn)(比如 with 語句)同樣很重要和簸。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末彭雾,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子锁保,更是在濱河造成了極大的恐慌薯酝,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,084評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件爽柒,死亡現(xiàn)場(chǎng)離奇詭異吴菠,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)浩村,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門做葵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人穴亏,你說我怎么就攤上這事蜂挪。” “怎么了嗓化?”我有些...
    開封第一講書人閱讀 163,450評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵棠涮,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我刺覆,道長(zhǎng)严肪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,322評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮驳糯,結(jié)果婚禮上篇梭,老公的妹妹穿的比我還像新娘。我一直安慰自己酝枢,他們只是感情好恬偷,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,370評(píng)論 6 390
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著帘睦,像睡著了一般袍患。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上竣付,一...
    開封第一講書人閱讀 51,274評(píng)論 1 300
  • 那天诡延,我揣著相機(jī)與錄音,去河邊找鬼古胆。 笑死肆良,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的逸绎。 我是一名探鬼主播惹恃,決...
    沈念sama閱讀 40,126評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼棺牧!你這毒婦竟也來了座舍?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,980評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤陨帆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后采蚀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體疲牵,經(jīng)...
    沈念sama閱讀 45,414評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,599評(píng)論 3 334
  • 正文 我和宋清朗相戀三年榆鼠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了纲爸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,773評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡妆够,死狀恐怖识啦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情神妹,我是刑警寧澤颓哮,帶...
    沈念sama閱讀 35,470評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站鸵荠,受9級(jí)特大地震影響冕茅,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,080評(píng)論 3 327
  • 文/蒙蒙 一姨伤、第九天 我趴在偏房一處隱蔽的房頂上張望哨坪。 院中可真熱鬧,春花似錦乍楚、人聲如沸当编。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽忿偷。三九已至,卻和暖如春词渤,著一層夾襖步出監(jiān)牢的瞬間牵舱,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評(píng)論 1 269
  • 我被黑心中介騙來泰國打工缺虐, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留芜壁,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,865評(píng)論 2 370
  • 正文 我出身青樓高氮,卻偏偏與公主長(zhǎng)得像慧妄,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子剪芍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,689評(píng)論 2 354

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

  • 閉包就是指一個(gè)有權(quán)訪問另外一個(gè)函數(shù)作用域中的變量的函數(shù)塞淹。--《JavaScript高級(jí)程序第三版》 本人對(duì)于閉包初...
    車大棒閱讀 485評(píng)論 0 0
  • 寫這篇文章的原因很簡(jiǎn)單,臨近畢業(yè)罪裹,不得不考慮找工作的問題饱普,面臨著就業(yè)的壓力,開始在前輩的教育下状共,開始刷題之旅套耕,從l...
    BeLLESS閱讀 798評(píng)論 0 2
  • 王福朋 - 博客園 —— 《 深入理解javascript原型和閉包》 目錄:深入理解javascript原型和閉...
    帥而不花__美而不浪閱讀 1,446評(píng)論 0 2
  • 閉包(closure)是Javascript語言的一個(gè)難點(diǎn),也是它的特色峡继,很多高級(jí)應(yīng)用都要依靠閉包實(shí)現(xiàn)冯袍。 一、變量...
    zock閱讀 1,075評(píng)論 2 6
  • 闌珊風(fēng)雪入樓中 依曲水碾牌,流觴夢(mèng) 渡河無捷岸 梅雨沐驚風(fēng) 臨云不懼山高冷 一夜如姜翁 誰曾獨(dú)釣 江雪朦朧 問天外 來...
    婉約的風(fēng)閱讀 393評(píng)論 0 0