深入理解闭包系列第二篇——从执行环境角度看闭包
前面的話
本文從執(zhí)行環(huán)境的角度來分析閉包,先用一張圖開宗明義,然后根據(jù)圖示內(nèi)容對代碼進行逐行說明,試圖對閉包進行更直觀的解釋
?
圖示
?
說明
下面按照代碼執(zhí)行流的順序?qū)υ搱D示進行詳細說明
function foo(){var a = 2;function bar(){console.log(a);}return bar; } var baz = foo(); baz();【1】代碼執(zhí)行流進入全局執(zhí)行環(huán)境,并對全局執(zhí)行環(huán)境中的代碼進行聲明提升(hoisting)
【2】執(zhí)行流執(zhí)行第9行代碼var baz = foo();,調(diào)用foo()函數(shù),此時執(zhí)行流進入foo()函數(shù)執(zhí)行環(huán)境中,對該執(zhí)行環(huán)境中的代碼進行聲明提升過程。此時執(zhí)行環(huán)境棧中存在兩個執(zhí)行環(huán)境,foo()函數(shù)為當前執(zhí)行流所在執(zhí)行環(huán)境
【3】執(zhí)行流執(zhí)行第2行代碼var a = 2;,對a進行LHS查詢,給a賦值2
【4】執(zhí)行流執(zhí)行第7行代碼return bar;,將bar()函數(shù)作為返回值返回。按理說,這時foo()函數(shù)已經(jīng)執(zhí)行完畢,應(yīng)該銷毀其執(zhí)行環(huán)境,等待垃圾回收。但因為其返回值是bar函數(shù)。bar函數(shù)中存在自由變量a,需要通過作用域鏈到foo()函數(shù)的執(zhí)行環(huán)境中找到變量a的值,所以雖然foo函數(shù)的執(zhí)行環(huán)境被銷毀了,但其變量對象不能被銷毀,只是從活動狀態(tài)變成非活動狀態(tài);而全局執(zhí)行環(huán)境的變量對象則變成活動狀態(tài);執(zhí)行流繼續(xù)執(zhí)行第9行代碼var baz = foo();,把foo()函數(shù)的返回值bar函數(shù)賦值給baz
【5】執(zhí)行流執(zhí)行第10行代碼baz();,通過在全局執(zhí)行環(huán)境中查找baz的值,baz保存著foo()函數(shù)的返回值bar。所以這時執(zhí)行baz(),會調(diào)用bar()函數(shù),此時執(zhí)行流進入bar()函數(shù)執(zhí)行環(huán)境中,對該執(zhí)行環(huán)境中的代碼進行聲明提升過程。此時執(zhí)行環(huán)境棧中存在三個執(zhí)行環(huán)境,bar()函數(shù)為當前執(zhí)行流所在執(zhí)行環(huán)境
在聲明提升的過程中,由于a是個自由變量,需要通過bar()函數(shù)的作用域鏈bar() -> foo() -> 全局作用域進行查找,最終在foo()函數(shù)中也就是代碼第2行找到var a = 2;,然后在foo()函數(shù)的執(zhí)行環(huán)境中找到a的值是2,所以給a賦值2
【6】執(zhí)行流執(zhí)行第5行代碼console.log(a);,調(diào)用內(nèi)部對象console,并從console對象中l(wèi)og方法,將a作為參數(shù)傳遞進入。從bar()函數(shù)的執(zhí)行環(huán)境中找到a的值是2,所以,最終在控制臺顯示2
【7】執(zhí)行流執(zhí)行第6行代碼},bar()的執(zhí)行環(huán)境被彈出執(zhí)行環(huán)境棧,并被銷毀,等待垃圾回收,控制權(quán)交還給全局執(zhí)行環(huán)境
【8】當頁面關(guān)閉時,所有的執(zhí)行環(huán)境都被銷毀
?
總結(jié)
從上述說明的第5步可以看出,由于閉包bar()函數(shù)的原因,雖然foo()函數(shù)的執(zhí)行環(huán)境銷毀了,但其變量對象一直存在于內(nèi)存中,就是為了能夠使得調(diào)用bar()函數(shù)時,可以通過作用域鏈訪問到父函數(shù)foo(),并得到其變量對象中儲存的變量值。直到頁面關(guān)閉,foo()函數(shù)的變量對象才會和全局的變量對象一起被銷毀,從而釋放內(nèi)存空間
由于閉包占用內(nèi)存空間,所以要謹慎使用閉包。盡量在使用完閉包后,及時解除引用,以便更早釋放內(nèi)存
//通過將baz置為null,解除引用 function foo(){var a = 2;function bar(){console.log(a);//2 }return bar; } var baz = foo(); baz(); baz = null; /*后續(xù)代碼*/ 創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的深入理解闭包系列第二篇——从执行环境角度看闭包的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: array专题9
- 下一篇: sqlserver数据库修复