前情提要:
?JS變量分為兩種:局部變量和全局變量
*函數(shù)內(nèi)部可以直接訪問(wèn)全局變量。

*而函數(shù)外部卻無(wú)法訪問(wèn)函數(shù)內(nèi)部的變量。

引申出一個(gè)問(wèn)題:如何在函數(shù)外部訪問(wèn)函數(shù)內(nèi)部的變量呢?

? ?既然inner可以讀取outer中的局部變量,那么只要把inner作為返回值,我們就可以在outer外部讀取它的內(nèi)部變量了嗎 。
? ? 于是“閉包”概念誕生。
什么是閉包?
? ? ?閉包就是:能夠讀取其他函數(shù)內(nèi)部變量的【函數(shù)】,在javascript中,只有函數(shù)內(nèi)部的子函數(shù)才能讀取局部變量,所以閉包可以理解成“定義在一個(gè)函數(shù)內(nèi)部的【函數(shù)】“。在本質(zhì)上,閉包是將函數(shù)內(nèi)部和函數(shù)外部連接起來(lái)的橋梁。
? ? ?進(jìn)一步說(shuō):一個(gè)函數(shù)和對(duì)其周圍狀態(tài)(lexical environment,詞法環(huán)境)的引用捆綁在一起(或者說(shuō)函數(shù)被引用包圍),這樣的組合就是閉包(closure)。也就是說(shuō),閉包讓你可以在一個(gè)內(nèi)層函數(shù)中訪問(wèn)到其外層函數(shù)的作用域。在 JavaScript 中,每當(dāng)創(chuàng)建一個(gè)函數(shù),閉包就會(huì)在函數(shù)創(chuàng)建的同時(shí)被創(chuàng)建出來(lái)。
閉包特點(diǎn):1:引用外部函數(shù)變量對(duì)象中的值;2:在外部函數(shù)的外部被調(diào)用


閉包的作用
? ? 閉包可以用來(lái)【間接的訪問(wèn)一個(gè)變量】,相當(dāng)于把【變量隱藏】,如果寫全局變量就可能會(huì)被人隨意修改,所以我們可以定義一個(gè)不能讓人【直接訪問(wèn)】到的局部變量,然后再寫一個(gè)【函數(shù)】作為訪問(wèn)器把變量暴露出去。
從堆棧的角度看閉包
? ? ??在程序運(yùn)行時(shí),計(jì)算機(jī)會(huì)為應(yīng)用程序分配一定的內(nèi)存空間;應(yīng)用程序則會(huì)自行分配所獲得的內(nèi)存空間,其中一部分被用于記錄程序中正在調(diào)用的各個(gè)函數(shù)的運(yùn)行情況,這就是函數(shù)的調(diào)用棧。常規(guī)的函數(shù)調(diào)用總是會(huì)在調(diào)用棧最上層添加一個(gè)新的堆棧幀(stack frame,也翻譯為“棧幀”或簡(jiǎn)稱為“幀”),這個(gè)過(guò)程被稱作“入?!被颉皦簵!保ㄒ饧窗研碌膸瑝涸跅m敚?。
? ?回顧基礎(chǔ):基本變量的值一般都是存在棧內(nèi)存中,而對(duì)象類型的變量的值存儲(chǔ)在堆內(nèi)存中,棧內(nèi)存存儲(chǔ)對(duì)應(yīng)空間地址。


? ? ? 首先在全局執(zhí)行環(huán)境中,我們可以訪問(wèn)到變量a和fn,進(jìn)入fn時(shí)棧內(nèi)存會(huì)push一個(gè)fn的執(zhí)行環(huán)境,這個(gè)環(huán)境里有變量b和函數(shù)fn1,也可以訪問(wèn)到全局的執(zhí)行環(huán)境。進(jìn)入fn1時(shí),棧內(nèi)存會(huì)push一個(gè)fn1的執(zhí)行環(huán)境,這個(gè)執(zhí)行環(huán)境下無(wú)變量,但是可以訪問(wèn)到fn執(zhí)行環(huán)境和全局環(huán)境下的變量。
? ? ?隨著fn1()執(zhí)行完畢,fn1的執(zhí)行環(huán)境被杯銷毀,接著執(zhí)行完fn(),fn的執(zhí)行環(huán)境也會(huì)被銷毀,只剩全局的執(zhí)行環(huán)境下,現(xiàn)在沒(méi)有b變量,和fn1函數(shù)對(duì)象了,只有a 和 fn(函數(shù)聲明作用域是window下)。
? ? 在函數(shù)內(nèi)訪問(wèn)某個(gè)變量是根據(jù)函數(shù)作用域鏈來(lái)判斷變量是否存在的,而函數(shù)作用域鏈?zhǔn)浅绦蚋鶕?jù)函數(shù)所在的執(zhí)行環(huán)境棧來(lái)初始化的,因?yàn)槌绦蛟谠L問(wèn)變量時(shí),是【向底層棧一個(gè)個(gè)找】的。
閉包實(shí)際情況:

當(dāng)執(zhí)行完result = outer這一句之后,outer函數(shù)并沒(méi)有被銷毀,因?yàn)樗锩娴淖兞咳员籭nner的函數(shù)作用域鏈所引用,當(dāng)執(zhí)行完inner之后,inner和outer的執(zhí)行環(huán)境才會(huì)被銷毀。
閉包的優(yōu)缺點(diǎn)
它的最大用處有兩個(gè):
? ? ? ? ?1、可以讀取函數(shù)內(nèi)部的變量
? ? ? ? ?2、就是讓這些變量的值始終保持在內(nèi)存中。
閉包的缺點(diǎn)就是常駐內(nèi)存會(huì)【增大內(nèi)存使用量】,并且使用不當(dāng)很容易造成內(nèi)存泄露?!禞avaScript高級(jí)編程》書(shū)中建議:由于閉包會(huì)攜帶包含它的函數(shù)的作用域,因?yàn)闀?huì)比其他函數(shù)占用更多內(nèi)容,過(guò)度使用閉包,會(huì)導(dǎo)致內(nèi)存占用過(guò)多。
如果不是因?yàn)槟承┨厥馊蝿?wù)而需要閉包,在沒(méi)有必要的情況下,在其它函數(shù)中創(chuàng)建函數(shù)是不明智的,因?yàn)殚]包對(duì)腳本性能具有負(fù)面影響,包括處理速度和內(nèi)存消耗。
閉包的使用場(chǎng)景
1:setTimeout/setInterval
2:回調(diào)函數(shù)(callback)
3:事件句柄(event handle)
參考博客:
1:https://zhuanlan.zhihu.com/p/22486908
2:https://www.cnblogs.com/sandaizi/p/11582488.html
3:http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html