今天忽然想到一个问题,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.
总结:
我的看法,说得不对之处欢迎评论指正 : )
参考文章: