前情提要:
?JS變量分為兩種:局部變量和全局變量
*函數(shù)內(nèi)部可以直接訪問全局變量铐伴。
*而函數(shù)外部卻無法訪問函數(shù)內(nèi)部的變量宿稀。
引申出一個(gè)問題:如何在函數(shù)外部訪問函數(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ù)外部連接起來的橋梁。
? ? ?進(jìn)一步說:一個(gè)函數(shù)和對其周圍狀態(tài)(lexical environment刃榨,詞法環(huán)境)的引用捆綁在一起(或者說函數(shù)被引用包圍)弹砚,這樣的組合就是閉包(closure)。也就是說枢希,閉包讓你可以在一個(gè)內(nèi)層函數(shù)中訪問到其外層函數(shù)的作用域桌吃。在 JavaScript 中,每當(dāng)創(chuàng)建一個(gè)函數(shù)苞轿,閉包就會(huì)在函數(shù)創(chuàng)建的同時(shí)被創(chuàng)建出來茅诱。
閉包特點(diǎn):1:引用外部函數(shù)變量對象中的值;2:在外部函數(shù)的外部被調(diào)用
閉包的作用
? ? 閉包可以用來【間接的訪問一個(gè)變量】搬卒,相當(dāng)于把【變量隱藏】瑟俭,如果寫全局變量就可能會(huì)被人隨意修改,所以我們可以定義一個(gè)不能讓人【直接訪問】到的局部變量契邀,然后再寫一個(gè)【函數(shù)】作為訪問器把變量暴露出去摆寄。
從堆棧的角度看閉包
? ? ??在程序運(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欠橘,也翻譯為“棧幀”或簡稱為“幀”),這個(gè)過程被稱作“入椣帜眨”或“壓椝嘈”(意即把新的幀壓在棧頂)。
? ?回顧基礎(chǔ):基本變量的值一般都是存在棧內(nèi)存中述暂,而對象類型的變量的值存儲(chǔ)在堆內(nèi)存中痹升,棧內(nèi)存存儲(chǔ)對應(yīng)空間地址。
? ? ? 首先在全局執(zhí)行環(huán)境中畦韭,我們可以訪問到變量a和fn疼蛾,進(jìn)入fn時(shí)棧內(nèi)存會(huì)push一個(gè)fn的執(zhí)行環(huán)境,這個(gè)環(huán)境里有變量b和函數(shù)fn1艺配,也可以訪問到全局的執(zhí)行環(huán)境察郁。進(jìn)入fn1時(shí),棧內(nèi)存會(huì)push一個(gè)fn1的執(zhí)行環(huán)境转唉,這個(gè)執(zhí)行環(huá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)在沒有b變量末荐,和fn1函數(shù)對象了,只有a 和 fn(函數(shù)聲明作用域是window下)新锈。
? ? 在函數(shù)內(nèi)訪問某個(gè)變量是根據(jù)函數(shù)作用域鏈來判斷變量是否存在的甲脏,而函數(shù)作用域鏈?zhǔn)浅绦蚋鶕?jù)函數(shù)所在的執(zhí)行環(huán)境棧來初始化的,因?yàn)槌绦蛟谠L問變量時(shí)妹笆,是【向底層棧一個(gè)個(gè)找】的块请。
閉包實(shí)際情況:
當(dāng)執(zhí)行完result = outer這一句之后,outer函數(shù)并沒有被銷毀拳缠,因?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)存泄露∨恋ǎ《JavaScript高級編程》書中建議:由于閉包會(huì)攜帶包含它的函數(shù)的作用域朝捆,因?yàn)闀?huì)比其他函數(shù)占用更多內(nèi)容,過度使用閉包懒豹,會(huì)導(dǎo)致內(nèi)存占用過多芙盘。
如果不是因?yàn)槟承┨厥馊蝿?wù)而需要閉包,在沒有必要的情況下脸秽,在其它函數(shù)中創(chuàng)建函數(shù)是不明智的儒老,因?yàn)殚]包對腳本性能具有負(fù)面影響,包括處理速度和內(nèi)存消耗记餐。
閉包的使用場景
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