在試圖弄清這個(gè)問題之前,先要理解棧內(nèi)存、堆內(nèi)存和預(yù)處理。
占用內(nèi)存,不會銷毀的閉包實(shí)例
例1:
var num = 12;
function fn() {
var num = 100;
return function () {
console.log(num);
}
}
var f = fn();
f();
例1的圖示
未被占用的堆內(nèi)存才會被銷毀
所以,正如圖中橢圓形關(guān)鍵點(diǎn)中說明的那樣,堆內(nèi)存xxxfff111被返回給了全局變量f,而全局變量只有在窗口關(guān)閉的時(shí)候才會銷毀,因此堆內(nèi)存xxxfff111將一直被占用而不會銷毀,定義它的局部作用域A也不會被銷毀。
例2
var oDiv = document.getElementById("div1");
~function() {
oDiv.onclick = function() {
}
}();
這段代碼的特點(diǎn)是:私有作用域給DOM元素的事件綁定一個(gè)方法。
例2的圖示:
正如圖中橢圓形關(guān)鍵點(diǎn)所說,標(biāo)簽對象的屬性里面會自帶一個(gè)onclick的屬性,未被賦值時(shí)其值為null。那么,在自執(zhí)行函數(shù)執(zhí)行的時(shí)候,其創(chuàng)建的作用域所占用的堆內(nèi)存xxxfff111同樣也會被全局的堆內(nèi)存xxxfff000占用(這里要注意,是堆內(nèi)存占用堆內(nèi)存),所以堆內(nèi)存xxxfff111和棧內(nèi)存A都不會被銷毀。
不占用內(nèi)存,立即銷毀的實(shí)例
只需要將例1稍作修改。
例3:
function fn(){
var num = 100;
return function(){
console.log(num);
}
}
fn(); //主要修改在這里
例3的圖示
由于在函數(shù)fn中,xxxfff111是被return的,所以棧內(nèi)存A的預(yù)解釋不會處理xxxfff111,它只在fn函數(shù)執(zhí)行的時(shí)候才會生成,而函數(shù)fn的棧內(nèi)存A每次被執(zhí)行之后都會被銷毀。
暫時(shí)占用內(nèi)存,延時(shí)銷毀的閉包實(shí)例
將例3稍作修改,就變成了延時(shí)銷毀的閉包實(shí)例。
例4:
function fn(){
var num = 100;
return function(){
}
}
fn()(); //這里到底發(fā)生了什么?其實(shí)是執(zhí)行了一次fn之后,把返回的子函數(shù)有執(zhí)行了一次,所以在子函數(shù)執(zhí)行的時(shí)候,棧內(nèi)存fn()是不能銷毀的,但是子函數(shù)執(zhí)行完畢后因?yàn)闆]有被占用,所以最終還是要被銷毀的,所以最終fn()還是會被銷毀的。
例4的圖示:
正如途中橢圓形關(guān)鍵點(diǎn)處所說,fn()()
的意思是在執(zhí)行完fn()
之后再把返回的值函數(shù)執(zhí)行一遍。因此在子函數(shù)執(zhí)行的時(shí)候,堆內(nèi)存xxxfff111被占用了,相應(yīng)的棧內(nèi)存A也將保留。
可堆內(nèi)存xxxfff111中保存的子函數(shù)在執(zhí)行完成之后還是會被銷毀,接著堆內(nèi)存xxxfff111就作為未被占用的堆內(nèi)存而被銷毀,最終棧內(nèi)存A也會被銷毀。
所以,棧內(nèi)存在執(zhí)行完之后會被保留一段時(shí)間,這段時(shí)間等于其子函數(shù)執(zhí)行的時(shí)間。
參考資料:
JavaScript高級程序設(shè)計(jì)(第三版)。