内存泄漏会对浏览器造成很大的压力,之前隐隐约约有听说过“如果闭包不处理是一定存内存泄漏的”,这是真的吗?是为什么呢?
函数作用域链
-
创建函数outerFun()时,会创建一个预先包含全局变量对象的作用域链,保存在内部的[[Scope]]属性中。
-
调用函数outerFun()时,为此函数创建一个执行环境。
-
然后复制函数的[[Scope]]属性中的对象构建起执行环境的作用域链。
-
此后,创建一个活动对象,推入执行环境作用域链的前端([0]位置)。
此时执行环境的作用域链中包含两个变量对象:全局变量对象(第3步) 、 局部活动对象(第4步)。
函数中访问一个变量时,就会从作用域链中搜索具有相应名字的变量。
函数执行完后,局部活动对象被销毁,内存中仅保存全局作用域。
一、什么是闭包
1、父函数里面嵌套的子函数,子函数访问父函数的局部变量。
2、通过return将子函数暴露到全局作用域,子函数就形成闭包。
3、通过闭包,父函数的局部变量没有被销毁,可通过闭包去调用 ,但同时,这个局部变量也不会被全局变量污染。
二、简单的闭包
function addNum() {
var num = 0;
return function () {
num++
console.log(num);
};
}
addNum()();//1
三、关于闭包的内存泄漏
常见闭包
内部函数 被 外部函数 包含时,内部函数 会将 外部函数 的局部活动对象添加到自己的作用域链中。而由于 内部匿名函数 的作用域链 在引用 外部包含函数 的活动对象 ,即使addNum执行完毕了,它的活动对象还是不会被销毁!
即,addNum的执行环境作用域链都销毁了,它的活动对象还在内存中留着呢。
并且根据垃圾回收机制,被另一个作用域引用的变量不会被回收。
所以,除非内部的匿名函数解除对活动变量的引用(解除对匿名函数的引用),才可以释放内存。
*老浏览器(主要是IE6)由于垃圾回收有问题导致很容易出现内存泄漏,现在的主流浏览器已经不会发生这种问题。
// 创建函数 还未调用
var creatFun = addNum()
// 调用函数
var result = creatFun()
// 解除对匿名函数的引用
creatFun = null
循环引用
许多人对闭包和内存泄露的关系有误解,认为闭包一定会引起内存泄漏,其实是不对的,我来谈谈我对他们关系的理解。闭包和内存泄露有关系的地方是,使用闭包的同时比较容易形成循环引用,如果闭包的作用域链中保存着一些DOM节点,这时候就有可能造成内存泄露。这主要是程序员容易引起的bug,不是浏览器的问题。
如果要解决循环引用带来的内存泄露问题,我们只需要把循环引用中的变量设为null即可。将变量设置为null意味着切断变量与它此前引用的值之间的连接。当垃圾收集器下次运行时,就会删除这些值并回收它们占用的内存。
比如:
function showId() {
var el = document.getElementById("app")
el.onclick = function(){
console.log(el.id) // 这样会导致闭包引用外层的el,当执行完showId后,el无法释放
}
}
// 改成下面
function showId() {
var el = document.getElementById("app")
var id = el.id
el.onclick = function(){
console.log(id) // 这样会导致闭包引用外层的el,当执行完showId后,el无法释放
}
el = null // 主动释放el
}
参考文档[1]:https://blog.csdn.net/qq_44991493/article/details/116133386
参考文档[2]:https://blog.csdn.net/kk211814/article/details/88323872