今天忽然想到一個問題,let的塊級做用域,以及閉包的變量引用功能頗有意思(這腦洞咋聯想到一塊兒的,囧)。。閉包的使用會影響瀏覽器的GC過程。那麼:es6
先看一個經典例子,循環異步打印問題(沒耐心的直接跳最後一個實例(^▽^))瀏覽器
// 想異步打印1到5
for(var i=1; i<=5;i++) {
setTimeout(function(){
console.log("print: " + i);
}, i*1000)
}
// 結果
print: 6
print: 6
print: 6
print: 6
print: 6
複製代碼
因爲是異步調用打印函數,因此等調用這個函數時,循環已經結束,i變成了6,因此連着打印5個6。bash
第二種狀況,若是用let 來聲明i,let 和var 相比至少有以下特性:閉包
// 1到5
for(let i=1; i<=5;i++) {
setTimeout(function(){
console.log("print: " + i);
}, i*1000)
}
// 結果
print: 1
print: 2
print: 3
print: 4
print: 5
複製代碼
這種狀況下直接經過let, 實際上給每一次回調函數的註冊,建立了一個閉包,因此打印正常。異步
第三種狀況,經過手動建立閉包也能夠實現相似效果。每次循環內,返回一個函數引用當時的變量 i,這樣其實是從新分配了內存來存儲i 的值,而不是單純的引用內存地址。 尼瑪內存蹭蹭往上漲,不過這麼點數據徹底不用擔憂函數
// 1到5
for(var i=1; i<=5;i++) {
setTimeout((function(){
var b = i; //install timer的時候引用 i 而且return 一個函數
return function(){
console.log("print: " + b);
}
})(), i*1000)
}
複製代碼
這個例子很好地說明了閉包對內部變量內存地址的保留做用(循環1w次就深度複製了1w份i )。但閉包和全局變量的不當使用可能會致使內存泄漏,內存居高不下甚至標籤頁直接掛掉。工具
JS 變量在瀏覽器內存中是否被GC 回收要看這個變量所在做用域的生命週期和變量是否被別人引用:post
JS 對象(引用類型)是存儲在內存堆heap中,能夠經過Chrome Debug Tool的 Profile 工具生成Heap SnapShot 來查看。ui
最後看一個活生生的實例,不出意外分分鐘內存佔用1G:this
function Test()
{
this.obj= {};
this.index = 1;
this.timer = null;
var cache = []; // 內部變量,內存隱患...
this.timer = window.setInterval(() =>{
this.index += 1;
this.obj = {
val: '_timerxxxxxbbbbxx_' + this.index,
junk: [...cache]
};
cache.push(this.obj);
}, 1);
console.warn("create Test instance..");
}
test = new Test(); // JS對象開啓定時器不斷分配內存
複製代碼
囉嗦幾句,這個例子的關鍵在於內部變量cache被外部的異步函數(定時器)引用。 若是不清除定時器,只是把Test類的實例手動設爲null,也無濟於事,cache還會繼續佔用內存。
Test.prototype.destroy = function(){
clearInterval(this.timer);
}
function d() {
// 取消定時器並銷燬Test 實例
test.destroy();
test = null;
console.warn("destroyed test instance..");
}
複製代碼
清除內部變量cache的引用後,內存堆大小馬上降低了40MB.
總結:
我的看法,說得不對之處歡迎評論指正 : )
參考文章: