添加原生事件不移除,为什么会导致内存泄漏

6,060 阅读2分钟

添加原生事件不移除,为什么会导致内存泄漏?

比如下面代码:

var button = document.getElementById('button');
function onClick(event) {
    button.innerHTML = 'text';
}
button.addEventListener('click', onClick);

给元素button添加了一个事件处理器onClick, 而处理器里面使用了button的引用。而老版本的 IE 是无法检测 DOM 节点与 JavaScript 代码之间的循环引用,因此会导致内存泄漏

如今,现代的浏览器(包括 IE 和 Microsoft Edge)使用了更先进的垃圾回收算法,已经可以正确检测和处理循环引用了。换言之,回收节点内存时,不必非要调用 removeEventListener 了。

另外介绍一下常见的容易导致内存泄漏的点

  • 意外的全局变量 (如果必须使用全局变量存储大量数据时,确保用完以后把它设置为 null 或者重新定义。与全局变量相关的增加内存消耗的一个主因是缓存。缓存数据是为了重用,缓存必须有一个大小上限才有用。)
  • 被遗忘的计时器,比如下面示例代码, 尽管这个定时器不再需要,里面的回调也不再需要,可是计时器回调函数并没有被回收,这样someResource,如果存储了大量的数据,也是无法被回收。 因此需要把定时器清除。
var someResource = getData();
setInterval(function() {
    var node = document.getElementById('Node');
    if(node) {
        // 处理 node 和 someResource
        node.innerHTML = JSON.stringify(someResource));
    }
}, 1000);
  • 脱离DOM的引用(当你保存了一个dom的引用,然后将该dom从html中删除后,你应该将这个引用赋为null,否则GC不会回收,这个dom仍然在内存中。保存 DOM 元素引用的时候,要小心谨慎。
var elements = {
    button: document.getElementById('button'),
    image: document.getElementById('image'),
    text: document.getElementById('text')
};
function doStuff() {
    image.src = 'http://some.url/image';
    button.click();
    console.log(text.innerHTML);
    // 更多逻辑
}
function removeButton() {
    // 按钮是 body 的后代元素
    document.body.removeChild(document.getElementById('button'));
    // 此时,仍旧存在一个全局的 #button 的引用
    // elements 字典。button 元素仍旧在内存中,不能被 GC 回收。
}
  • 闭包

闭包包含这外面函数的活动对象,无法被GC回收。