都說閉包是javaScript中一個近乎神話的概念,我剛好碰到這個神話,想著怎么把它講成一個故事!javaScript中閉包無處不在的,我們要做的是識別和擁抱它。
閉包到底是什么?
當(dāng)函數(shù)可以記住并訪問所在的詞法作用域(不論函數(shù)是否在當(dāng)前的詞法作用域內(nèi)執(zhí)行),就產(chǎn)生了閉包。當(dāng)然,這是官話,學(xué)術(shù)語氣太強了理解起來很難受,所以我用自己的理解+接地氣的語言來詮釋一下這句話,我覺得在此同時,我們可以將閉包的缺陷一起了解一下。下面我們來看兩段代碼:


這里的兩段代碼執(zhí)行的都是將原有的a變量重新賦值并打印出來,我們先來看(當(dāng)函數(shù)可以記住并訪問所在的詞法作用域(不論函數(shù)是否在當(dāng)前的詞法作用域內(nèi)執(zhí)行),就產(chǎn)生了閉包)這句話怎么理解,看圖2。
首先要明白,產(chǎn)生閉包的先決條件是函數(shù)的嵌套使用!
其次再來理解概念:函數(shù) wn() 包含在 foo() 的內(nèi)部,它的詞法作用域能訪問到 foo() 的內(nèi)部作用域,我們將 wn() 對象本身作為返回值返回給 foo(),foo() 執(zhí)行后,其返回值(也就是內(nèi)部的 wn() 函數(shù))賦值給了psc,并調(diào)用執(zhí)行了 psc() ,實際上只是通過不同的標識符引用,來調(diào)用了內(nèi)部函數(shù) wn()。到這里這些文字應(yīng)該都比較好理解對吧?。。。『玫谋牬笱劬聪旅孢@句話很重要:函數(shù) foo() 并不是自執(zhí)行函數(shù),圖2代碼的執(zhí)行順序并不是從上往下依次執(zhí)行的這個也能理解的吧?!代碼是執(zhí)行到最后的 psc() 調(diào)用的時候才返回去找 psc 是什么,好的找到了 psc 是函數(shù) foo() 的賦值,然后再去找 foo() 是什么,找到 foo() 這個函數(shù)的時候才依次從上向下執(zhí)行內(nèi)部的代碼。剛剛我們就分析過了,圖2的代碼其實際上只是通過不同的標識符引用最終調(diào)用的是 wn() 函數(shù),在這個過程中,wn() 即實現(xiàn)了調(diào)用輸出,而它的詞法作用域又保存完成整——這就是閉包的概念。

無論通過何種手段將內(nèi)部函數(shù)(這里指的是 wn() )傳遞到所在的詞法作用域以外,它都會持有對原始定義的作用域的引用,無論在何處執(zhí)行這個函數(shù),都涉及到使用閉包這一概念。
——另外,圖1放上來的作用一直沒說,其作用就是用來對比圖2,3。瀏覽器的引擎有垃圾回收機制(想要了解原理的自行去谷歌)。圖1中當(dāng)foo() 調(diào)用完成,該函數(shù)內(nèi)部將全局變量 a 賦值成了 2,當(dāng)我們可以拿到這個為 2 的 a 來用時,事實上,這個 foo() 函數(shù)已經(jīng)功成名就,這個時候,雖然代碼還是在我們寫的js文件中,但是對于瀏覽器來說,這個已經(jīng)執(zhí)行過的 foo() 函數(shù)宣布退隱江湖,也不再給它分配作用域空間了,foo() 的內(nèi)容也不再被使用了,引擎默認這樣做的意義是釋放瀏覽器不再使用的內(nèi)存空間,這相當(dāng)于是引擎的自動優(yōu)化功能。而?閉包,這個東西,一旦形成卻是能阻止引擎的這一默認事件的發(fā)生,因為閉包形成了自己穩(wěn)定完整的內(nèi)部作用域,引擎已經(jīng)拿它沒有辦法了(打不過,動不了它,這個還是跟引擎的工作原理有關(guān)系),這個穩(wěn)定的內(nèi)部作用域?qū)⒂谰玫奶峁┙o? wn() 函數(shù)使用,所以拜 wn() 所聲明的位置所賜,它將永久擁有并在任何時間使用這個涵蓋 foo() 內(nèi)部的作用域。——這就是閉包的缺點,不能釋放不需要使用的作用域,如果一個項目中這樣的閉包使用很多的話,對瀏覽器性能的消耗將大大提升。emmmmmmmmmm
不知道這樣講的夠不夠清楚,還不夠通俗的話我emmmm.....絞盡奶汁再想想怎么解釋?