2

我是 JavaScript 的初学者,但我最近遇到了一个问题,我可以用一个小例子重现:

function TimerTest(panelId)
{
    this.PanelID = panelId;
}

TimerTest.prototype.Start = function()
{
    var self = this;

    $(document.getElementById(this.PanelID)).everyTime("1s", "Test", function(){
        self.Print();
    });
}

TimerTest.prototype.Print = function()
{
    $("#debug").append("<div>triggered...</div>");
}

function Remove()
{
    $("#main").empty();
    $("#main").html("I'm done!");
}

$(document).ready(function(){
    var timer = new TimerTest("timerpanel");
    timer.Start();
});

在 JSFiddle 中运行

调用 Remove() 后,该对象似乎仍然存在并打印“已触发...” :(

我使用 HTML5 的历史功能通过 AJAX 加载内容。我基本上遇到的是,我有一个带有标记的嵌入式谷歌地图和一个计时器,每隔一段时间就会更新这些标记。当离开该页面时,我仍然可以在 FireBug AJAX 调用中看到更新标记。这对我来说意味着即使它的面板从 DOM 中删除,对象也没有被破坏?JQuery 关于 empty() 的文档虽然说它删除了所有 DOM 元素及其数据。

我有点困惑。我知道在离开之前我可以在对象上调用“stopTime()”,但这不是我的问题。示例中的计时器就在那里,因此我实际上可以看到 TimerTest 对象仍然存在(即我的 Google 地图仍然存在!)。

4

2 回答 2

1

即使您#main要从 DOM 中删除该元素,TimerTest仍然会保留对它的引用。元素可以存在,但不能在 DOM 中(参见detach()示例)。

对象仅在不存在对它的引用时才由垃圾收集器回收。

jQuery 的empty()方法将从 DOM 中删除元素,并将删除 jQuery 存储的有关通过data()方法添加的元素的所有数据,以及事件处理程序。

于 2012-05-18T09:11:21.667 回答
1

试图运行你的小提琴,Chrome 抱怨 jquery.offput.ca,它称之为“已知分发恶意软件”的网站。如果我去看那个 jQuery 插件的演示,我会得到同样的警告。因此,我无法使用该实际代码。 在您更新时,我可以使用您的实际代码,因此请参阅此答案的最后,以使用您的原始代码获得答案。不过,我将保留非计时器插件的答案。

元素可能会或可能不会保留在内存中,这完全取决于计时器插件的工作方式。不过,重要的是,无论元素是否在内存中,都不会停止计时器。你需要代码来做到这一点。

我从未使用过该计时器插件,但大概它有一些方法可以告诉计时器停止,可能是通过您对已附加计时器的元素的 jQuery 包装器进行的调用。因此,在您的Remove函数中,您需要一种方法来识别带有计时器的元素,以便您可以停止它们。

这是一个使用计时器插件的示例,以演示这些概念。以下是我的做法:

  1. 当我创建setInterval计时器时,我记得一个名为data-timer-handle. 您可能需要也可能不需要,但您需要在元素上放置某种标记,以便告诉计时器插件停止。

  2. Remove中,在清除之前#main,我找到它具有属性的任何子项data-timer-handle并清除间隔计时器。

现场复制| 来源

HTML:

<div id="main">
    <div id="timerpanel">I'm the timer panel</div>
</div>
<input type="button" id="removeButton" value="Remove It">
<div id="debug"></div>

JavaScript:

function TimerTest(panelId)
{
    this.PanelID = panelId;
}

TimerTest.prototype.Start = function()
{
    var self = this;

    // Start the timer
    var handle = setInterval(function() {
        self.Print();
    }, 1000);

    // Remember the handle on a data-timer-handle property
    // on the element.
    $(document.getElementById(this.PanelID)).attr("data-timer-handle", handle);
}

TimerTest.prototype.Print = function()
{
    $("#debug").append("<div>triggered...</div>");
}

function Remove()
{
    var main = $("#main");
    // Find any elements with timers on them, stop them
    main.find("[data-timer-handle]").each(function() {
        clearInterval($(this).attr("data-timer-handle"));
    });
    main.html("I'm done!");
}

$(document).ready(function(){
    var timer = new TimerTest("timerpanel");
    timer.Start();
    $("#removeButton").click(function() {
        Remove();
    });
});

如果我正在编写一个将计时器附加到元素的计时器插件,我肯定会在元素中添加一些东西(data-*属性、类等),以便您再次找到它们。但是,我还提供了一个选项,用于在元素从 DOM 中删除时自动取消计时器(这样您就不需要查找和停止它们),方法是让每个计时器滴答声检查元素是否仍然存在随附的。(默认情况下我可能不会启用它,但它会是一个选项。)


在下面回复您的评论:

不过,我对您的代码有疑问:它确实停止了计时器,但是我怎么知道它实际上破坏了TimerTest-object?计时器只是我说的一种方式。AFAIK 计时器本身没有对TimerTest-object 的引用,那么为什么该对象仍然存在?您可以通过修改 Print() 来打印“this.PanelID”`

啊,但是计时器确实保留了对TimerTest实例的引用:您传递给计时器的函数是一个闭包。它引用了创建它的范围内的所有内容,包括self引用该对象的变量TimerTest(当然,否则self.Print()将不起作用)。因此,只要该函数存在,它就会将TimerTest实例保存在内存中。

那么是什么将这个函数保存在内存中呢?计时器对它的引用。只要计时器还活着,它就有一个对该函数的引用,并将该函数保存在内存中。在我上面的示例中,clearInterval删除了对函数的引用,使其符合垃圾回收的条件;这意味着该函数不再将它关闭的东西保存在内存中。如果该函数是唯一对实例具有突出引用的事物TimerTest,则意味着该TimerTest实例有资格进行垃圾回收。

在我的代码中,没有任何东西将元素保留在内存中(您正在使用的计时器插件可能不是这样),但是TimerTest只要浏览器的setInterval东西正在使用该函数,该函数就会使该实例保持活动状态。

更多探索:


现在你的小提琴没有链接到那个(也许)狡猾的网站:

我惊讶地发现,计时器插件似乎并没有在元素中添加任何东西,以便再次轻松找到。没关系,这对我们来说是微不足道的。

更改Start以标记元素 - 这里我使用了一个类:

TimerTest.prototype.Start = function()
{
    var self = this;

    $("#" + this.PanelID)
        .addClass("hastimer")
        .everyTime("1s", "Test", function(){
            self.Print();
    });
};

(我还使用 jQuery 来查找元素,而不是document.getElementById。并且您希望在函数表达式的末尾使用分号。)

并更改Remove为在该类的所有元素上停止计时器:

function Remove()
{
    var main = $("#main");
    main.find(".hastimer").stopTime();
    main.html("I'm done!");
}

empty(在调用之前无需调用htmlhtml调用empty。)

更新的小提琴

于 2012-05-18T10:05:43.840 回答