一:什么是内存泄漏
在计算机科学中,由于疏忽或错误造成程序未能释放已经不再使用的内存,并非指内存在物理内存泄漏是内存消失,而是应用程序分配某段内存后,由于设计疏忽,导致在释放该段内存之前就失去了对该段内存的控制,从而造成了内存的浪费。指在JS中已经分配内存地址的对象由于长时间未进行内存释放或无法清除,造成了长期占用内存,使得内存资源浪费,最终导致运行的应用响应速度变慢以及最终崩溃的情况。
二:常见的内存泄漏情况
- JS具有自动垃圾回收机制,也就是说,执行环境会负责管理代码执行过程中使用的内存。
- 原理:垃圾收集器会定期找出那些不再继续使用的变量,然后释放其内存;关于垃圾回收机制的了解可以看看我的上一篇博客对垃圾回收机制的理解。
1.意外的全局变量
- (1)未声明直接赋值
js在非严格模式下允许对未声明的变量进行赋值,则未声明的变量默认为全局变量,因此函数执行后也不会被回收。
function fn(){
num = 3;
console.log(num);
}
fn() // 3
console.log(num) //3 此时num为全局变量
- (2)this创建的全局变量
function fn(){
this.str = 'hello';
console.log(str);//hello
}
fn() // fn调用自己,this指向window
console.log(num) //hello
2.未被清空的定时器
子节点清除后定时器还会不停的走。如不及时清除定时器,内部执行的回调函数将不会被回收,就会造成内存泄露。
<div class="father">
<span class="son">haihaihai</span>
</div>
let father = document.querySelector('.father')
let son = document.querySelector('.son')
setInterval(function(){
father.removeChild(son);
},1000)
3.没有清理的DOM元素
- (1)引用元素没有清理
Dom节点删除了,但是节点的引用还在,导致无法对其所占内存的回收,如上文第二个情况中,若不删除Dom节点的引用,无效的Dom引用将继续常驻在内存中:
const abc = document.querySelector('.abc');
document.body,removeChild(abc);//删除DOM
console.log(abc) //但是还存在,能够打印出来dom元素,没有被回收
//解决办法
abc = null;
console.log(abc) //null 解除引用
- (2)事件的绑定没有解除
function fn(){
console.log('hello')
}
//事件绑定
document.addEventListener('click',fn);
//该监听事件无法被销毁:注册事件时最好不要使用匿名函数,一旦将匿名函数添加到全局的事件监听当中,会导致无法销毁这个监听事件
document.addEventListener('click',()=>{
console.log('无法被销毁的匿名函数')
})
解决办法:移除事件绑定
document.removeEventListener('click',fn);
4.滥用闭包
函数的内部函数保存了局部变量obj,以至于在函数getObj执行完毕后,局部变量obj未能销毁,最终导致内存泄露。如果大量使用闭包存储变量,就会增大内存的消耗。
function bindEvent(){
let obj = {a:1}
let getObj = () => {
console.log(obj.a) //1 闭包内引用obj,obj不会被释放
};
//解决办法
obj = null;
}