23

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

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

假设 this.$div2 被附加到另一个 div this.$div1。如果我打电话

this.$div1.remove();

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

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

那么我应该关心正确删除所有事件和这样的 HTML 元素吗?或者只是删除“根”元素(this.$div1)就可以解决问题?

4

3 回答 3

17

this.$div2附加到this.$div1. 如果我调用this.$div1.remove();并稍后丢失我的实例的引用,SomeClassSomeClass实例是否会被垃圾收集?

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

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

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

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

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

出于这个原因,.removejQuery 方法( code ) 在内部调用了(internal)cleanData方法,该方法分离了所有事件侦听器。

那么我应该关心正确删除所有事件和这样的 HTML 元素吗?或者只是删除“根”元素(this.$div1)就可以解决问题?

是的,调用removejQuery 包装器会自动删除所有事件(从所有子元素中)和 DOM 节点。

于 2012-12-23T20:35:11.643 回答
7

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

最简洁的答案是不!至少在 99% 的情况下,这无关紧要,因为与网页使用的总内存相比,一个 DOM 元素使用的内存微不足道。

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

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

相互存储引用的 JavaScript 对象和 DOM 元素导致 Internet Explorer 的垃圾收集器不回收内存,从而导致内存泄漏

在此处输入图像描述

你可以在这里读更多关于它的内容

这种特殊的泄漏主要是 IE<8 的历史意义,但打破循环链接的一个很好的例子是避免使用变量that,而是使用代理委托将事件处理程序的上下文更改为某些特定的上下文。

ECMA 第 5 种绑定方法在涉及 DOM 事件处理程序时退出有用的更改上下文,这是一个基于您的代码的简单处理程序,不使用变量闭包:

this.$div2.click((function() {
        this.someMethod2();
    }).bind(this));
于 2012-12-23T20:52:09.347 回答
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)
}

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

于 2012-12-25T01:49:07.803 回答