垃圾回收和 DocumentFragment

3
我读过一篇关于内存泄漏的文章,其中垃圾回收器逻辑总结为:
1. 垃圾回收器构建一个“根”列表。根通常是在代码中保留引用的全局变量。在JavaScript中,“window”对象就是一个可以充当根的全局变量的例子。窗口对象始终存在,因此垃圾回收器可以将其及其所有子项视为始终存在(即不是垃圾)。
2. 检查并标记所有根为活动状态(即不是垃圾)。也要递归地检查所有子元素。从根可以到达的所有内容都不被视为垃圾。
3. 所有未标记为活动状态的内存块现在都可以视为垃圾。垃圾收集器现在可以释放该内存并将其返回给操作系统。
此外,MDN指出DocumentFragment不是活动DOM树的一部分。
文档片段接口表示没有父级的最小化文档对象。它用作Document的轻量级版本,其存储了由节点组成的文档结构段,就像标准文档一样。主要区别在于,因为文档片段不是活动文档树结构的一部分,对片段进行更改不会影响文档、引起重排或产生任何性能影响。
逐步了解逻辑,但非常希望有人可以为我提供更多解释。使用以下示例并说明:
1. 在使用DOM引用后将其设为null被认为是一种良好的实践。
2. 是否需要将对DocumentFragment和包含它的元素的引用设为null。

function usefulFunction() {
  let existingNode = document.querySelector(`.existing`)
  
  let createdNode = document.createElement(`ul`)
  let fragment = document.createDocumentFragment();
  let browsers = ['Firefox', 'Chrome', 'Opera', 
      'Safari', 'Internet Explorer'];

  browsers.forEach(function(browser) {
      var li = document.createElement('li');
      li.textContent = browser;
      fragment.appendChild(li);
  });
    
  existingNode.appendChild(createdNode)
  createdNode.appendChild(fragment)
  
  fragment = null
  createdNode = null
  existingNode = null
}

usefulFunction()
<div class="existing"></div>

更新片段

let existingNode

function helperFunction(object) {
  let createdNode = document.createElement(`div`)
  createdNode.innerHTML = `Hello, I am a beautiful div`
  
  existingNode.appendChild(createdNode)
  existingNode = null 
}

function usefulFunction() {
  existingNode = document.querySelector(`.existing`)
  let fragment = document.createDocumentFragment();
  let browsers = ['Firefox', 'Chrome', 'Opera', 
      'Safari', 'Internet Explorer'];

  browsers.forEach(function(browser) {
      var li = document.createElement('li');
      li.textContent = browser;
      fragment.appendChild(li);
  });
      
  existingNode.appendChild(fragment)
  helperFunction()
}


usefulFunction()
<div class="existing"></div>

1个回答

3
如果您正确使用局部变量,通常在使用后无需将其置空。当您离开函数的范围时,变量会消失,并且它们所引用的任何对象(如果没有从某些仍在作用域内的变量引用)都将成为垃圾。文档片段不是从DOM引用的,只是从变量引用的,因此当变量被销毁时,片段可以被垃圾回收。
这是您应该谨慎使用全局变量的原因之一。它们只应用于需要持久存在的数据,例如保存应用程序状态。
请注意,在您的第一个示例代码中,将变量置空对垃圾回收没有影响,因为它们包含的所有节点和片段都附加到了DOM上。但是如果它们没有被附加到DOM上,则这些对象将在函数结束时变成垃圾,因此在返回之前无需将变量置为空。
在第二个代码片段中,如果您从DOM中删除该元素,则应将existingNode置为空。否则,全局变量将防止该节点被垃圾回收。但是,如果该节点预计在应用程序的生命周期内是永久性的,则无需担心该变量。

谢谢Barmar的精彩解释 :) 我做了一些额外的工作,如果您能说这是您所说的实际情况之一,并且如果我错了,请纠正逻辑以显示有效情况,或者您可以建议一篇好的文章供晚间阅读吗? :感谢: - volna
1
没有区别。您仍然将创建的所有内容附加到DOM,因此它无法被垃圾回收。变量是无关紧要的。 - Barmar
非常抱歉我没有表达清楚。在第二个片段中,我最感兴趣的是现有节点变量本身(我只是重用了“旧”片段),因为您解释说全局变量应该用于存储数据。所以,如果有人在某个时候分配DOM对象的引用,那么将其设置为null是否应该被视为有效情况? - volna
现在我明白了。我已经更新了答案以应对这个变化,请查看最后一段。 - Barmar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接