接上回我写了一篇关于闭包的博客《学习JavaScript之闭包》, 最后谈到闭包导致的问题时留了一个尾:

在IE9以下的浏览器中会有内存泄漏的问题。

今天的博客就继续探索一下内存泄漏的问题。

浅谈JavaScript垃圾回收机制

1.标记清除

一开始垃圾收集器会给内存中的所有变量做一个标记,之后当程序运行进入相应的环境时,会去掉环境中的变量和被环境中变量引用的变量标记;当退出该环境后,无法再被访问的变量又重新被标记,这些被重新标记的变量就会被垃圾收集器回收。

2.引用计数

记录每个值被引用的次数,当被引用次数为0时该值才会被回收。如果某个值被其他对象引用(赋给某个变量),引用次数+1;如果不再被引用则-1。

闭包导致的内存泄漏怎么产生的

在IE9以前的浏览器中,DOM对象的垃圾回收机制就是使用引用计数(虽然JS引擎是用标记清除),因此一旦产生循环引用(比如下面这个例子)内存就会一直被占用而无法回收。

function a () {
  var div = document.getElementById('myTitle')
  div.onclick = function () {
    alert(div.id)
  }
}

这段代码中变量div中保存了一个HTML元素对象,又创建了一个事件处理程序,其中还直接引用了div.id形成了循环引用,而且这个匿名函数只要存在就能利用闭包的特性访问到div,因此这个HTML元素的引用至少是1。

如何解决内存泄漏的问题

首先要解除循环引用:把div.id赋值给一个变量,然后在匿名函数中引用该变量,因为变量只包含值而不存在对div直接引用,所以解除了循环引用。但是,由于闭包的特性使得其仍保存着a函数的活动对象(等于间接地在引用HTML元素),因此还需要手动解除div对HTML元素的引用。用代码写出来就是:

 function a () {  
   var div = document.getElementById('myTitle')  
   var id = div.id // 解除循环引用
   div.onclick = function () {    
     alert(id)  
   }
   div = null // 手动解除闭包对外部函数活动对象的引用
 }

这样DOM对象的引用就变成了0,也就会被正常地回收了。

博客地址:lbj的前端之路

原文链接:学习JavaScript之内存泄漏


LiuBoJun
264 声望18 粉丝

专注于知识库产品开发,在富文本编辑器领域有丰富经验