土豆博客

JavaScript中的内存泄漏及处理方法

# 一、概述

内存泄露是指一块被分配的内存既不能使用,又不能回收,直到浏览器进程结束,内存泄漏的最终结果是堆栈溢出,js的垃圾回收机制就是为了防止内存泄漏的。

下面列举4类常见的内存泄漏问题

# 二、常见的内存泄漏

2.1 意外的全局变量

未定义的变量会在全局对象创建一个新变量,如下。

function foo(arg) {
    bar = "this is a hidden global variable";
}

函数 foo 内部忘记使用 var ,实际上JS会把bar挂载到全局对象上,意外创建一个全局变量。

function foo(arg) {
    window.bar = "this is an explicit global variable";
}

另一个意外的全局变量可能由 this 创建。

function foo() {
    this.variable = "potential accidental global";
}
foo(); // 调用foo,但是this指向的是全局对象(window)

解决方法

在 JavaScript 文件头部加上 'use strict',使用严格模式避免意外的全局变量,此时上例中的this指向undefined。如果必须使用全局变量存储大量数据时,确保用完以后把它设置为 null 或者重新定义。

2.2 被遗忘的计时器或回调函数

计时器setInterval代码很常见

var someData = getData();
setInterval(function() {
    var node = document.getElementById('Node'); 
    if(node) {
        node.innerHTML = JSON.stringify(someData)); 
    }
}, 1000);

如果 id 为 Node 的元素从 DOM 中移除, 该定时器仍会存在, 同时, 因为回调函数中包含对 someData的引用, 定时器外面的 someData也不会被释放。除非终止定时器

在Vue中,如果组件中使用了定时器,需要在生命周期的beforeDestroy 中做对应销毁处理

var id = new Vue({
    el: "#app",
    data: {
        timer: null
    },
    methods: {
        todo() {
            this.timer = setInterval(() => {
                //do something...
            }, 1000);
        }
    },
    beforeDestroy() {
        clearInterval(timer)
    },
})

2.3 dom监听的事情没有被清除

事件有绑定就应该有移除

var element = document.getElementById('button');
function onClick(event) {
    element.innerHtml = 'text';
}
element.addEventListener('click', onClick);
//解除监听
element.removeEventListener('click', onClick);

2.4 闭包引起的内存泄漏

闭包实际上非常容易造成JavaScript对象和DOM对象的隐蔽循环引用。来看看下面的例子:

function example(){
    var element = document.getElementById("btn"); //①
    element.onclick = function() {
        alert("This is a leak!"); //②
    }
}

以上函数example() 中用匿名函数创建了一个闭包。

第①句:声明一个element变量并指向了一个DOM对象(其id为"btn")JS(element)——> DOM(btn)

第②句:该DOM对象的onclick属性引用了匿名函数闭包,而闭包可以引用外部函数example() 的整个活动对象,包括element DOM(btn.onclick) ---->JS(element) 由此形成了JavaScript对象和DOM对象的隐蔽循环引用。

解决方法:

常用的解决方法就是在JavaScript代码段运行完之时将形成循环引用的JavaScript对象手动设置为空,切断引用。

修改的例子如下:

function example(){
    var element = document.getElementById("btn"); 
    element.onclick = function() {
        alert("This is a leak!"); 
    }
    element = null //加上这一句代码
}
# 三、vue中如何处理
  • 如果在mounted/created 钩子中绑定了DOM/BOM 对象中的事件,需要在beforeDestroy 中做对应解绑处理
  • 如果在mounted/created 钩子中使用了第三方库初始化,需要在beforeDestroy 中做对应销毁处理
  • 如果组件中使用了定时器,需要在beforeDestroy 中做对应销毁处理
  • 模板中不要使用表达式来绑定到特定的处理函数,这个逻辑应该放在处理函数中
  • 如果在mounted/created 钩子中使用了$on,需要在beforeDestroy 中做对应解绑($off)处理
  • 某些组件在模板中使用 事件绑定可能会出现泄漏,使用$on 替换模板中的绑定
code
top

扫码添加,一起进步

wechat-code

为了保障最佳预览体验,博客已不支持IE浏览器的访问,邀请您使用以下现代高级浏览器。

谷歌浏览器(推荐) 火狐浏览器

注:如果你使用的是360,QQ等双核浏览器,请开启极速模式