Add/remove JavaScript 个侦听器(垃圾收集器)

Add/remove listeners on JavaScript ( garbage collector )

我有一个关于 DOM object 的 adding/removing 听众的快速问题。 我想问一下垃圾收集器是否能够在从页面中删除元素时收集内存。

示例:带有 children(<li>)

列表的 <ul> 标签
var ul = document.getElementById('someParent');
var children = ul.children;
var someFunction = function() {};

for(var i = 0; i < children.length; i++) {
  children[i].addEventListener('click', someFunction);
}


// This is where I am not sure, would the garbage collector be able to collect
// the memory allocated on adding listener on children,
// if I remove the ul tag?
ul.remove();

ul.remove(); 将从 DOM 中删除 ul 元素及其所有子元素。但是只要您引用了这些侦听器、li 元素和 ul 元素,事件侦听器的内存就不会被释放。您在变量 childrensomeFunctionul.

中有哪些

如果你想让垃圾收集器清理所有这些,你可以这样做:

ul.remove();
children = null;
someFunction = null;
ul = null;

这样变量 children 将不会保存对这些元素的引用,如果您的代码中没有任何其他变量保存这些元素的引用,垃圾收集器将收集它们。 someFunction也是如此。请注意,ul 元素包含对其子元素和侦听器的所有引用,因此 ul 也必须被清除。

如果有人能为我确认这一点,我将不胜感激。

据我了解,是的它将被垃圾回收。根据MDN Memory Management

The main notion garbage collection algorithms rely on is the notion of reference. Within the context of memory management, an object is said to reference another object if the former has an access to the latter (either implicitly or explicitly). For instance, a JavaScript object has a reference to its prototype (implicit reference) and to its properties values (explicit reference).

本质上,如果 DOM 找不到对所述元素的引用,它将在不久的将来某个时候对其进行垃圾回收。查看 DOM 关于 .remove() 工作方式的标准规范,它将 运行 一系列步骤,这些步骤将设置 parent 和兄弟姐妹的新索引,删除所有对元素.

因为没有对所述元素的引用,所以它将被垃圾回收。在您的示例中,您仅将 eventListeners 添加到您创建的特定 <ul> 元素的 children。当你删除一个元素时,你也删除了它的 children 的所有引用(我认为这在上面的 link 中的步骤中更清楚,他们实际上将 parent 索引设置为指向对自己)。

编辑:@contrabit 是对的。我没有看到您将 children 存储在变量中。只要它们是对它们的引用,它们就不会被垃圾回收。

一点也不,您可以在其他地方访问 ul 变量。下面的示例显示了这一点。

var ul = document.getElementById('someParent');
ul.remove();
console.log(ul); // ul and all li tags
document.body.appendChild(ul); // ul appears again

这个例子不是一个正常的例子。正常情况是您想在事件中访问 DOM 引用 "button click"。只要事件未解除绑定,该引用将始终可用。例如:

var ul = document.getElementById('someParent');
var myButton = document.getElementById('myButton');
myButton.addEventListener('click', function () {
    ul.innerHTML += '<li>Some text</li>'
});
ul.remove();
myButton.click(); // no exception when execting button click event

为了避免 JS 中的内存泄漏:

  1. 使用 jQuery 确保没有 DOM 引用 例如。
  2. 如果您在应用程序中使用 DOM 引用,请将它们设置为空或 未使用时未定义。

因此您可以按如下方式稍微修改您的活动。

myButton.addEventListener('click', function () {
    // check ul belongs to visible DOM
    if (!document.body.contains(ul)) {
        ul = null;
        return;
    }
    ul.innerHTML += '<li>Some text</li>'
});