MST

星途 面试题库

面试题:JavaScript内存泄漏场景及如何避免

列举出至少三种JavaScript中可能会导致内存泄漏的场景,并详细说明每种场景下为什么会发生内存泄漏,以及对应的避免内存泄漏的方法。
20.5万 热度难度
编程语言JavaScript

知识考点

AI 面试

面试题答案

一键面试

意外的全局变量

  1. 原因:在JavaScript中,如果变量未声明就直接赋值,JavaScript会自动在全局对象(浏览器环境下是window)上创建一个属性。这些意外的全局变量不会被垃圾回收机制回收,因为全局对象的生命周期与页面的生命周期相同,只要页面不关闭,全局对象就不会被销毁,从而导致内存泄漏。例如:
function leak() {
    a = 1; // 这里没有使用var、let或const声明a,a会成为全局变量
}
  1. 避免方法:严格模式('use strict';)可以防止意外创建全局变量,在严格模式下,未声明的变量赋值会抛出错误。同时,始终使用varletconst声明变量。

闭包引起的内存泄漏

  1. 原因:闭包是指函数可以访问并操作其词法作用域外部的变量。当闭包被长时间保留在内存中(例如作为全局变量),并且闭包内部引用了一些大的对象,即使这些对象在外部代码中不再使用,由于闭包对它们的引用,垃圾回收机制无法回收这些对象,从而导致内存泄漏。例如:
function outer() {
    let largeObject = { /* 一个大对象 */ };
    return function inner() {
        return largeObject;
    };
}
let closure = outer();
// 这里即使outer函数执行完毕,largeObject也不会被回收,因为closure闭包引用了它
  1. 避免方法:尽量减少不必要的闭包使用。如果必须使用闭包,确保在闭包不再需要时,手动解除对不再使用对象的引用。例如,在上述例子中,可以在适当的地方将closure设为null,让垃圾回收机制有机会回收largeObject

DOM元素引用

  1. 原因:当JavaScript代码持有对DOM元素的引用,而这些DOM元素从文档中移除(例如通过removeChild方法),但JavaScript中的引用仍然存在时,这些DOM元素及其所有子元素不会被垃圾回收机制回收,导致内存泄漏。例如:
let element = document.getElementById('myElement');
// 假设之后myElement从DOM树中移除,但这里仍然保留了对它的引用
  1. 避免方法:当从DOM中移除元素时,同时将JavaScript中对该元素的引用设为null,这样垃圾回收机制就可以回收这些元素。例如:
let element = document.getElementById('myElement');
// 移除元素
document.body.removeChild(element);
// 解除引用
element = null;

事件监听器未移除

  1. 原因:在DOM元素上添加事件监听器时,如果在元素被移除之前没有移除对应的事件监听器,即使元素从DOM中移除,事件监听器仍然存在于内存中,并且保持对该元素的引用,导致元素及其相关资源无法被垃圾回收,造成内存泄漏。例如:
let button = document.createElement('button');
function clickHandler() {
    console.log('Button clicked');
}
button.addEventListener('click', clickHandler);
document.body.appendChild(button);
// 假设之后移除button元素,但未移除事件监听器
document.body.removeChild(button);
  1. 避免方法:在移除DOM元素之前,先移除对应的事件监听器。例如:
let button = document.createElement('button');
function clickHandler() {
    console.log('Button clicked');
}
button.addEventListener('click', clickHandler);
document.body.appendChild(button);
// 移除事件监听器
button.removeEventListener('click', clickHandler);
// 移除元素
document.body.removeChild(button);