概念
内存泄漏,指在JS中已经分配内存地址的对象由于长时间未进行内存释放或无法清除,造成了长期占用内存,使得内存资源浪费,最终导致运行的应用响应速度变慢以及最终崩溃的情况。
- 1.循环引用
- 2.意外的全局变量
- 3.被遗忘的计时器或回调函数
- 4.脱离 DOM 的引用
- 5.闭包
循环引用
需要强调的一点就是,一旦数据不再使用,最好通过将其值设为 null 来释放其引用,这个方法被称为“解除引用”。
意外的全局变量
//第一种
function foo(arg) {
bar = "";
}
foo();
// 第二种
function foo() {
this.bar = "";
}
被遗忘的计时器和回调函数
let someResource = getData();
setInterval(() => {
const node = document.getElementById('Node');
if(node) {
node.innerHTML = JSON.stringify(someResource));
}
}, 1000);
// 使用之后 请调用 clearInterval 或者 clearTimeout。
DOM
在 IE8 以下的版本里,DOM 对象经常会跟 JavaScript 之间产生循环引用。看一个例子:
function setHandler() {
const ele = document.getElementById('id');
ele.onclick = function() {};
}
在这个例子中,DOM 对象通过 onclick 引用了一个函数,然而这个函数通过外部的词法环境引用了这个 DOM 对象,形成了循环引用。不过现在不必担心,因为所有现代浏览器都采用了标记-整理方法,避免了循环引用的问题。
除了这种情况,我们现在还会在其他时候在使用 DOM 时出现内存泄漏的问题。当我们需要多次访问同一个 DOM 元素时,一个好的做法是将 DOM 元素用一个变量存储在内存中,因为访问 DOM 的效率一般比较低,应该避免频繁地反问 DOM 元素。所以我们会这样写:
const button = document.getElementById('button');
当删除这个按钮时:
document.body.removeChild(document.getElementById('button'));
虽然这样看起来删除了这个 DOM 元素,但这个 DOM 元素仍然被 button 这个变量引用,所以在内存上,这个 DOM 元素是没法被回收的。所以在使用结束后,还需要将 button 设成 null。
另外一个值得注意的是,代码中保存了一个列表 ul 的某一项 li 的引用,将来决定删除整个列表时,我们自觉上会认为内存仅仅会保留那个特定的 li,而将其他列表项都删除。但事实并非如此,因为 li 是 ul 的子元素,子元素与父元素是引用关系,所以如果代码保存 li 的引用,那么整个 ul 将会继续呆在内存里。
闭包
合理的使用闭包,从而导致某些变量一直被留在内存当中。