JavaScript中常见的内存泄漏包含以下几种形式
- 意外的全局变量
- 未解除的监听事件
- 未清除的定时器或者回调函数
- 闭包或者循环引用
- 单例对象或全局对象引用循环
意外的全局变量
在函数内未使用var
, let
, 或 const
声明一个变量,它会被创建为一个全局变量,如果这个变量指向一个对象或数组,可能导致内存泄漏。
function func() {
obj = {} // 这将会创建一个全局变量,除非显式地删除,否则不会被垃圾回收
// something
// obj = null 删除变量的引用
}
未解除的监听事件
给一个DOM元素或者全局事件添加了事件监听器,但在移除该元素之前没有移除监听器,导致该元素及其相关的函数无法被垃圾回收。
const el = document.getElementById('el');
el.addEventListener('click', function onClick() {
// 处理点击事件
});
// 当元素或组件被移除时,应该移除事件监听器
// element.removeEventListener('click', onClick);
未清除的定时器或者回调函数
设置了setInterval
或setTimeout
,但是在不需要它们时没有清除。
let timerId
function startTimer() {
timerId = setInterval(function intervalCallback() {
// 定时器逻辑
}, 1000);
}
// 当不需要定时器时
// clearInterval(timerId);
闭包或者循环引用
函数内部创建了一个闭包,闭包内部引用了外部的变量,导致这些变量不能被垃圾回收。
function createLeak() {
const leak = {};
return function leakyClosure() {
// leakyClosure内部会引用leak,除非leakyClosure不再被调用或显式解除对leak的引用
}
}
const leakyFunction = createLeak();
// leakyClosure = null
单例对象或全局对象引用循环
对象间相互引用,导致它们不能被垃圾回收。
let objA = {};
let objB = {};
objA.reference = objB;
objB.reference = objA;
// 移除引用来打破引用循环
// delete objA.reference;
// delete objB.reference;
解决这些问题的方法通常是确保不再需要的变量被解除引用,例如将变量设置为null
,或者在不需要时清除定时器、事件监听器等。