下面就來說說閉包的一些基本概念和具體的形成過程恶守。
什么是閉包?
閉包就是既能重用一個變量衬吆,又可以保護(hù)變量不被污染的一種機(jī)制 收恢。下面就通過一個小例子來幫助大家理解閉包的作用武学。
var i=1;
function add(){
console.log(i++);
}
add() //1
add() //2
i=1;
add() // 1
add() // 2
假如上面的程序是用來取號排隊乘地鐵的,由于程序小哥哥的粗心大意伦意,在中間某個地方給全局的變量i重新賦值火窒,導(dǎo)致最后的取號重復(fù)啦,這是會導(dǎo)致非常不友好的后果的默赂,估計廣州的3號線天天得有人干架E嫱摇(這就是上面所說的變量受到污染)。
導(dǎo)致上面變量污染的最基本原因就是i是一個全局變量缆八,那么如果把i變成了函數(shù)內(nèi)部的局部變量,又會出現(xiàn)什么樣的情況呢疾捍?
function add(){
var i=1;
console.log(i++);
}
add() //1
add() //1
i=2;
add() // 1
add() // 1
上面這種情況也很好理解奈辰,因為每次調(diào)用完add()后,add自己的函數(shù)作用域AO就會被釋放乱豆,所以每次得到的結(jié)果都為1奖恰,這種情況估計地鐵里面的工作人員都要被****。很明顯,這是因為i為函數(shù)內(nèi)容自己的局部變量瑟啃,既不能重用的原因论泛。所以,閉包就是為了解決上面這種問題的(重用一個變量蛹屿,又可以保護(hù)變量不被污染)屁奏;
閉包的書寫步驟:
1.用外層函數(shù)包裹住受保護(hù)的變量和內(nèi)層函數(shù)
2.外層函數(shù)將內(nèi)層函數(shù)返回到外部
3.使用者調(diào)用外層函數(shù)獲得返回的內(nèi)層函數(shù)對象
function() outer{
var i=1;
return function(){
console.log(i++);
}
}
vat getNum=outer();
getNum();//1
getNum();//2
i=1;
getNum();//3
getNum();//4
由上面例子可以看出,變量i既能重用错负,又不會被污染坟瓢。那到底閉包底層的原理是什么呢?說得概括一點(diǎn)就是犹撒,外層函outer的作用域AO無法釋放折联。可能這句話大家看得很懵逼识颊,下面就通過圖解的方式诚镰,幫助大家簡單明了地理解閉包的底層原理。
1.首先程序在剛開始執(zhí)行之前祥款,會創(chuàng)建ECS執(zhí)行環(huán)境棧,用來存放一些變量或函數(shù)怕享。
2.程序開始執(zhí)行的時候,首先創(chuàng)建一個outer函數(shù)镰踏,和全局的getNum變量函筋,所以此時window中會存放入outer和getNum
3.當(dāng)outer函數(shù)被調(diào)用的時候,outer會放到ECS執(zhí)行環(huán)境棧中奠伪,并且創(chuàng)建一個outer的執(zhí)行環(huán)境AO跌帐,當(dāng)執(zhí)行到
var i=1這句代碼的時候,outer的AO中就有了自己的局部變量i绊率,
4.繼續(xù)往下執(zhí)行到return function() {console.log(i++);} 的時候谨敛,引擎就會創(chuàng)建一個函數(shù),此時這個函數(shù)的祖籍為outer滤否,不再是window脸狸,然后outer會把這個函數(shù)的地址返回給getNum,也就是說藐俺,這時候的getNum引用的就是outer內(nèi)部return出來的小函數(shù)炊甲,而內(nèi)部函數(shù)的祖籍又是outer,outer的parent又是window欲芹,此時就形成了一種相互抱團(tuán)的情況卿啡。
圖片中綠色的箭頭,就是相互之間作用域鏈關(guān)系菱父。
5.當(dāng)outer調(diào)用完以后颈娜,outer會從ECS中釋放剑逃,正常來說,outer的AO也會被跟著釋放官辽,但是請注意蛹磺,這時候outer的AO 被內(nèi)部的函數(shù)牽引著,因為內(nèi)部函數(shù)的scope指向outer的AO同仆,這就好比你手機(jī)上的微信一樣萤捆,只要你還用得著,就不會把它卸載掉一樣乓梨。也許你會問啦鳖轰,那內(nèi)部的function不會被釋放嗎?因為outer把內(nèi)部的函數(shù)給return出來啦扶镀,所以此時的內(nèi)部函數(shù)又被gerNum牽引著蕴侣,故內(nèi)部函數(shù)也釋放不了,閉包就此形成臭觉。
6.當(dāng)執(zhí)行g(shù)etNum()的時候昆雀,getNum就會被存放到ECS中,并且創(chuàng)造getNum的AO蝠筑,但是因為getNum沒有自己的局部變量i狞膘,當(dāng)函數(shù)內(nèi)部執(zhí)行到console.log(i++)的時候,他就會沿著作用域鏈什乙,往他的父集outer的AO中去尋找變量i挽封,輸出后并且執(zhí)行i++,outer中的i就變了2
7.當(dāng)執(zhí)行到 i=1的時候臣镣,因為全局window中沒有變量i辅愿,程序就會自動在全局中聲明一個全局變量i,并且其值為1忆某,此時并不會去操作outer函數(shù)AO中的局部變量i点待,所以outer執(zhí)行環(huán)境中的變量i并不會受到污染。
以上就是對閉包的概念和形成原理的一些解釋弃舒,希望可以幫助到一些需要的碼友們癞埠。