当使用监视器时,你需要确保做了一个明确的调用来删除它们。
幸运的是,大多数现代浏览器都会为你做这件事:即使你忘记删除监听器,当被监测对象变得无法访问,它们就会自动收集监测处理器。这是过去的一些浏览器无法处理的情况(例如旧的IE6)。
看下面的例子:
var element = document.getElementById('launch-button'); var counter = 0; function onClick(event) { counter++; element.innerHtml = 'text ' + counter; } element.addEventListener('click', onClick); // Do stuff element.removeEventListener('click', onClick); element.parentNode.removeChild(element); // Now when element goes out of scope, // both element and onClick will be collected even in old browsers // that don't handle cycles well.
由于现代浏览器支持垃圾回收机制,所以当某个节点变的不能访问时,你不再需要调用removeEventListener,因为垃圾回收机制会恰当的处理这些节点。
如果你正在使用jQueryAPI(其他库和框架也支持这一点),那么也可以在节点不用之前删除监听器。即使应用程序在较旧的浏览器版本下运行,库也会确保没有内存泄漏。
3:闭包
JavaScript开发的一个关键方面是闭包。闭包是一个内部函数,可以访问外部(封闭)函数的变量。由于JavaScript运行时的实现细节,可能存在以下形式泄漏内存:
var theThing = null; var replaceThing = function(){ var originalThing = theThing; var unused = function(){ if(originalThing)//对'originalThing'的引用 console.log(“hi”); }; theThing = { longStr:new Array(1000000).join('*'), someMethod:function(){ console.log(“message”); } }; }; setInterval(replaceThing,1000);
一旦replaceThing被调用,theThing会获取由一个大数组和一个新的闭包(someMethod)组成的新对象。然而,originalThing会被unused变量所持有的闭包所引用(这是theThing从以前的调用变量replaceThing)。需要记住的是,一旦在同一父作用域中为闭包创建了闭包的作用域,作用域就被共享了。
在这种情况下,闭包创建的范围会将someMethod共享给unused。然而,unused有一个originalThing引用。即使unused从未使用过,someMethod 也可以通过theThing在整个范围之外使用replaceThing。而且someMethod通过unused共享了闭包范围,unused必须引用originalThing以便使其它保持活跃(两封闭之间的整个共享范围)。这就阻止了它被收集。
所有这些都可能导致相当大的内存泄漏。当上面的代码片段一遍又一遍地运行时,你会看到内存使用率的不断上升。当垃圾收集器运行时,其内存大小不会缩小。这种情况会创建一个闭包的链表,并且每个闭包范围都带有对大数组的间接引用。