使用jquery创建和删除DOM的对象避免内存泄漏

时间:2012-05-30 16:24:54

标签: javascript jquery

假设您有一个组件,其中包括创建一些DOM节点(例如,使用jquery):

function PageFiller() {
}
PageFiller.prototype.fillPage = function () {
  this.dom = $("<div/>", {
    "class" : "hello",
    text : "Hello world"
  });
  $("body").append(this.dom);
}

假设另一个组件使用它来填充DOM,而不保留对“PageFiller”的引用

function Main() {
}
Main.prototype.start = function () {
   var filler = new PageFiller();
   filler.fillPage();
};
var main = new Main()
main.start();

不要假设在那之后(在同一范围内),有人残酷地清除了DOM:

$(".hello").remove();

在这种情况下,内存布局是什么?我是否用PageFiller实例泄露了内存?

我有点不确定PageFiller实例将在何时收集垃圾(如果它曾经发生过),因为它持有对表示不存在的DOM部分的jquery对象的引用? 或者它与DOM无关,因为根据我的理解,实际的 DOM对象生活在与常规JS变量不同的堆中(除非我在这里弄错了..)

更新:如果主要对象保留对PageFiller的引用,那么事物的变体就是:

Main.prototype.start = function () {
    this.filler = new PageFiller();
    filler.fillPage();
}

在这种情况下,Main对象具有对PageFiller对象的引用,该对象本身包含对JQ对象的引用;在'remove'之后,JQ对象仍然存在于内存中,但是指向不存在的DOM节点,对吧?

我是否正确地说,在原始示例中,只要main.start()完成:

  • 没有对象持有对PageFiller对象的引用,因此它被垃圾收集,并且pageFiller.dom对象没有被任何人引用,所以它也被垃圾收集了?
  • 实际的DOM元素显然仍然存在于内存中,但在一个单独的堆中,我真的不必担心

其他案例:

这次是一个小变化,事件处理程序使问题变得复杂:

PageFiller.prototype.fillPage = function () {
  var self = this;
  this.dom = ... // as usual 
  this.dom.click(function () {
    alert("Just clicked on dom generated by " + self);
  });
}

这一次,只是让PageFiller实例超出范围不足以回收其内存,因为它是由click处理程序中使用的闭包引用的。我在这里泄露了记忆吗?或者我可以信任$(“”)。remove()调用来终止对闭包的引用,从而杀死对PageFiller的最后一个引用?如果我在没有jquery的情况下删除了元素(使用原生DOM API?),那么事情会有什么不同吗?

这可能只是我希望的工作,但我只是想说服自己没有记忆可以从这种模式中泄露出来。

感谢。

1 个答案:

答案 0 :(得分:1)

<强>更新

删除filler.dom main.start()一次完成将减少它所持有的节点的引用计数,因为它不拥有节点(即弱属性)。

当您调用$().remove()时删除实际节点时,refcount会再次降低为零,并且可以进行垃圾回收。

所以,AFAICT你应该没有什么可担心的。