导致内存泄漏的因素及解决方式

导致内存泄漏的因素

一、全局变量

因为全局变量不被js垃圾回收机制所回收,所以在使用全局变量时要小心。避免在想使用局部变量因为疏忽导致该变量流失到全局,如未声明变量,却直接对其进行赋值,就会导致该变量在全局创建,如:

function fn() {
	arr = new Array(99999999);
}

fn();

此时,就会在全局创建一个非常大的数组arr,因为是全局变量,所以不被回收,就会一直存在于内存。
解决:
不要在未声明之前使用变量,或者开启严格模式,这样就会出现报错提示

function fn() {
	'use strict';
	arr = new Array(99999999);
}

fn();

在这里插入图片描述

二、闭包

因为闭包是在外部通过一些方式访问到函数内部的变量,即使在外部不再用了,但在外部也就产生了对函数内部变量的引用,该变量也就会一直被标记为活跃变量,存在于内存中,
**解决:**当该变量不在使用时手动给它置null

三、移除存在引用的DOM元素

当你移除某个节点时,本应释放该节点所占内存,但代码中依旧存在对该节点的引用,最终会导致该节点的内存无法被释放,如:

<div id="root">
    <div class="child">我是子元素div>
    <button>移除button>
div>
<script>
    let btn = document.querySelector('button')
    let child = document.querySelector('.child')
    let root = document.querySelector('#root')
    
    btn.addEventListener('click', function() {
        root.removeChild(child)
    })

script>

点击按钮之后会移除child节点,但是全局变量child依旧对其有引用,导致该节点的内存一直无法释放。
解决:

<div id="root">
    <div class="child">我是子元素div>
    <button>移除button>
div>
<script>
    let btn = document.querySelector('button')

    btn.addEventListener('click', function() {  
        let child = document.querySelector('.child')
        let root = document.querySelector('#root')

        root.removeChild(child)
    })

script>

当方法执行后会退出函数上下文,那么里面的局部变量也会child也会被清理掉,所以就不存在对child的引用了

四、控制台打印console.log

因为浏览器一直保存着我们所打印的信息,我们再能在打开控制台的时候看见我们打印的东西,所以它并不会释放内存

五、定时器

1、setTimeout的第一个参数传字符串而不是function会导致内存泄漏
原因:根据手册强调,不要传递字符串字面量,这会导致和eval一样的问题。具体为什么会因为传递字符串而不是函数会导致内存溢出的原因,经过查阅一些资料,发现 setTimeout和setInterval传入字符串时,若该字符串中存在以var声明的变量,那么该变量会在全局作用域下声明,根据JS垃圾回收机制,全局变量不能被回收。做个例子看一下:

var t = setTimeout(() => {
    var c = 10;
    console.log("c:", c);
}, 0);

var t2 = setTimeout(() => {
    console.log("c2:", c);
}, 2000);

结果:先输出c:10,2秒后提示c未定义,也就是说t2中的c是未定义的
在这里插入图片描述
换个方式:

var t = setTimeout('var c = 10; console.log("c:", c);', 0);

var t2 = setTimeout(() => {
    console.log("c2:", c);
}, 2000);

结果:先输出c:10,2秒后输出c2:10
在这里插入图片描述
所以足以见得,传入字符串给setTimeout,它中间var声明的变量会声明在全局作用域中,哪怕setTimeout已经销毁的,它依旧存在,所以会存在内存溢出的风险。
注意:由于输入的是字符串,它需要先调用JS解析器对字符串进行解析,再去执行解析后的JS代码。会更加占用浏览器内存,容易出现浏览器页面崩溃的情况。

2、忘记清除setInterval也会导致内存泄漏
例子:

function fn() {
    let largeObj = new Array(100000)

    setInterval(() => {
        let myObj = largeObj
    }, 1000)
}

fn();

执行fn之后如无setInterval,会退出fn的执行上下文,然后清除局部变量largeObj,但是由于setInterval没有清除,其内部一直存在变量myObj对largeObj的引用,导致largeObj不能被释放。
解决: 不要忘记清除setInterval

六、循环引用

当两个或多个对象存在循环引用时,根据JS垃圾回收机制的引用计数方式,它会标记互相循环引用的对象至少被引用了一次,所以不会释放这些内存
解决: 不要出现循环引用

七、未解绑事件监听器就删除了DOM节点

移除节点是,未先移除事件监听器,导致节点删除后,事件监听器依旧存在,此部分内存不会被释放
解决: 移除节点之前清除事件监听器

八,未删除无用的数据

定义了一些无用的数据,却没有删除,有可能导致内存溢出。
解决: 清理这些无用数据

排查内存泄漏方式可参考这位大佬

你可能感兴趣的:(前端,javascript)