Javascript中的事件处理程序,闭包和垃圾收集

时间:2012-12-14 13:56:17

标签: javascript jquery events garbage-collection

我的应用程序中没有遇到内存泄漏,但我担心将来可能出现的问题。我想知道是否做了这样的事情:

SomeClass.prototype.someMethod= function() {
    var that= this
    this.$div2.click(function() {
        that.someMethod2();
    });
}

让我们说这个。$ div2附加到另一个div。$ div1。如果我打电话

this.$div1.remove();

后来失去了我的SomeClass实例的引用SomeClass实例是否被垃圾收集?那个HTML元素呢。$ div2?这个。$ div2不会在DOM里面,因为它会附加到这个。$ div1。

我问这个因为。$ div2中的事件处理程序可能会保留对HTML元素的引用。$ div2并且还通过闭包保持对SomeClass实例的引用,因为变量"" 34。

那么我应该关心如何正确删除所有事件和HTML元素吗?或者只是删除" root" element(this。$ div1)解决了这个问题?

3 个答案:

答案 0 :(得分:15)

  

this.$div2附加到this.$div1。如果我调用this.$div1.remove();后来丢失了SomeClass实例的引用,那么SomeClass实例会被垃圾回收吗?

是的,当所有对它的引用都丢失了 - 也就是那些通过事件处理程序的引用 - 实例可以被垃圾收集。

  

HTML元素this.$div2怎么样? this.$div2不会在DOM中,因为它会附加到this.$div1

它是否当前附加到DOM无关紧要。如果某些不可收集的对象引用$div1,它也可以访问其子节点$div2和那个事件处理程序,因此从处理程序引用的实例将无法收集。

  

我问这个是因为this.$div2中的事件处理程序可能会保留对HTML元素this.$div2的引用,并且由于变量而通过闭包保留对SomeClass实例的引用“那个”。

这是一个循环引用, 应该由引擎很好地处理(当圈内的任何对象都没有从外部引用时,它可以被收集)。但是,(旧的?)Internet探测器在圆圈中涉及DOM对象时无法执行此操作。

出于这个原因,.remove jQuery methodcode)在内部调用(internal) cleanData method,它将所有事件侦听器分离。

  

那么我应该关心如何正确删除所有事件和HTML元素吗?或者只是删除“root”元素(这个。$ div1)解决了这个问题?

是的,在jQuery包装器上调用remove会自动删除所有事件(来自所有子元素)和DOM节点。

答案 1 :(得分:7)

  

我是否应该关心正确删除所有事件和HTML元素   像这样?

简短的回答是不!至少在99%的情况下,它无论以何种方式都无关紧要,因为与网页使用的整体内存相比,一个DOM元素使用的内存是微不足道的。

然而,释放通过处置不需要的对象所使用的内存总是一个好习惯,但是你不能说GC肯定会释放元素使用的内存,因为垃圾收集完全取决于浏览器!理论上GC只应在没有对DOM元素的引用时启动,至少是Chrome works的引用,但在JavaScript这样的语言中,你没有明确告诉你完成对象的运行时间在JavaScript中,事情变得如此迅速:函数可能会将对象传递给更多的函数,对象可能会作为另一个对象中的成员被保存,对象可能会通过闭包等引用,所以它完全取决于浏览器如何以及收集什么!

在你的情况下删除div1释放html文档并且元素不会在视图中呈现,实际上jQuery的remove方法负责删除附加的所有事件,expando属性和子元素与元素本身一起使用元素,但是在另一个对象中保留div1div2的引用,使两个DOM元素成为孤立元素!删除SomeClass实例变量会释放对DOM元素的所有引用,使它们成为垃圾收集的候选者,但是这里有一个棘手的that变量,它导致DOM元素通过SomeClass实例引用clusure!此问题在IE中称为Circular Reference

  

存储对一个引用的JavaScript对象和DOM元素   另一个原因导致Internet Explorer的垃圾收集器无法回收   内存,导致内存泄漏

enter image description here

You can read more about it here

这个特定的泄漏主要是历史感兴趣IE< 8,但打破循环链接的一个很好的例子是避免使用变量that,而是使用proxydelegate来改变事件处理程序对某些特定上下文的上下文。

在进入DOM事件处理程序时,

ECMA 5th bind method退出了有用的更改上下文,这里是一个基于代码的简单处理程序,而不使用变量闭包:

this.$div2.click((function() {
        this.someMethod2();
    }).bind(this));

答案 2 :(得分:1)

如果要动态创建元素,则为其分配事件。我认为你的代码不是一个很好的方法。你应该遵循这样的方式:

对于固定元素,如果需要事件,请使用这两个函数;第一个在构造函数中调用,第二个在析构函数中调用。

on_Events: function() {
   $('your_form').on('event_name', {element_Selector}, callback_function)
},
off_Events: function() {
   $('your_form').off('event_name', {element_Selector}, callback_function)
}

用于动态对象。在创建元素时添加事件,并在销毁元素之前删除这些事件。