【问题标题】:Detecting memory leaks in JavaScript检测 JavaScript 中的内存泄漏
【发布时间】:2009-12-09 05:42:24
【问题描述】:

我有这段代码,我在为项目构建的一些 JavaScript 组件上使用了它。现在我想知道以下代码是否存在内存泄漏。

哪个选项最合适,A 还是 B,还是有更好的方法?

var component = function(){
    var self = this; //A - not sure there's a leak here

    this.foo = function(){
        //var self = this; //B. I can do this but I want to use self in other method as well
        var dom = getElementById('someid');
        dom.onclick = function(){
            self.foo2(); // here I used the self reference
            //i cannot use this here, because it refer to dom
        }
    }

    this.foo2 = function(){
        var dom = getElementById('someid');
        dom.onclick = function(){
            self.foo2(); //here I used the self reference
            //i cannot use this here, because it refer to dom
        }
    }
};

// some usage

var c1 = new component();
c1.foo();

【问题讨论】:

  • 不知道——我对前世的内存泄漏检测是:写一个脚本,如果浏览器变得无响应,那就是内存泄漏。

标签: javascript memory-leaks


【解决方案1】:

内存消耗

这种方法的问题在于,所有方法的所有代码都将针对每个实例进行复制,这可能会导致大量内存消耗(除非围绕执行上下文进行漂亮的优化你不能依赖)。您所做的好处是点击处理程序直接调用您希望它调用的方法。

通常的做法是通过将方法放在原型上来在实例之间共享方法,并且实例在包含良好的闭包上下文中构建小型包装函数(例如,不会关闭无关数据的函数) -- 通常由帮助函数创建 -- 用于设置对实例的调用。这样,每个实例只复制少量代码,大部分代码是共享的。代价是每次点击都需要调用一个函数,然后转身并调用另一个函数,但坦率地说,除了非常紧凑的循环(点击处理程序不是),开销并不是真正的问题,而内存消耗确实可以成为当今网络应用程序中的一个问题。

在原型上设置函数在其他地方都有很好的介绍,通常通过帮助程序处理,让您比原始 JavaScript 更清晰、更简洁。有关包含完善的闭包上下文中的包装器构建器的示例,请查看 Prototype 中的 Function#bind 实现(或其他几个 JavaScript 库中的类似函数,如 MooTools、Closure 库等)

基本看起来像这样,但我实际上不会这样做:

var component = function() {
    this.boundFoo = bind(this, foo); // Remove this if you never use it as a handler
    this.boundFoo2 = bind(this, foo2);
};
component.prototype.foo = function() {
    var dom = getElementById('someid');
    dom.onclick = this.boundFoo2;
};
// (Isn't this exactly what foo did?)
component.prototype.foo2 = function() {
    var dom = getElementById('someid');
    dom.onclick = this.boundFoo2;
};
function bind(context, func) {
    return function() {
        func.apply(context, arguments);
    };
}

注意bind 如何接受上下文和函数,并返回一个new 函数,该函数将使用该上下文调用给定函数。

您还可以在分配函数时绑定函数,而不是将它们设置为实例上的属性,但如果您要重用它们(如上所示),那么保留一份副本即可有可能。

上面有一些问题(所有函数都是匿名的,这意味着你的工具无法帮助你)但是没有进入对象辅助函数,这是基本思想。

内存泄漏

“Crescent Fresh”指出我最初并没有真正解决内存泄漏。要解决的一个重要方面是,对于某些浏览器(主要是 IE 和衍生产品),当您完成事件处理程序(例如,离开页面时)unhook 非常重要。因此,如果您稍后不清除它们,您的两个 onclick 分配可能会在某些浏览器上出现内存泄漏。这是因为当 DOM 元素和 JavaScript 对象仍然没有被引用时,所涉及的浏览器不会处理清理循环引用(例如,它们相互引用但没有其他任何引用)。您必须断开 DOM 元素和 JavaScript 函数之间的链接,以确保两者都被清除。如果您使用它们的方法来附加事件处理程序,则像 Prototype 这样的库会在页面卸载时为您执行此操作;我不太了解其他库,无法评论他们是否这样做。

其他说明

有点过时,但是:

  1. 还要研究“事件委托”的概念——单击处理程序是使用委托的好地方,因为它们会冒泡。您可能会发现在容器级别只需要几个处理程序,而不是在元素级别需要数百个处理程序。
  2. 与其指定元素的onclick 属性(称为设置事件处理程序的“DOM0”样式),不如考虑使用更新的(90 年代后期)机制来执行此操作:addEventListener 在符合标准的浏览器上和 IE 上的 attachEvent。这样做的一大改进是可以为同一个元素上的同一个事件设置多个处理程序,而使用 DOM0 处理程序,如果有一个新的处理程序,则分配一个新的处理程序会破坏旧的处理程序。

【讨论】:

  • 一些优点。几个通用 cmets:您的答案实际上并没有解决内存泄漏问题。内存消耗是的,但是泄漏? (或者我是否遗漏了一些暗示?)WRT 内存消耗你没有提到的一个警告是,它需要实例化数千个这样的东西才能注意到prototyped 版本的任何显着内存使用(可能消耗与泄漏有关)。从 OPs 代码(getElementByIdonclick=)的外观来看,周围应该有 1 个这样的东西。最后,jQuery 实际上“缺少”bind 等效项。也许在 1.4 中它会出现,但我不确定。
  • @Crescent Fresh:关于泄漏的要点;固定的。感谢您提供 jQuery 信息。对我来说,完全不清楚 OP 不会将这些组件对象附加到页面上的数十个或数百个元素,和/或将此模式应用于他将大量创建的其他对象。可疑的模式,最好指出来。
猜你喜欢
  • 1970-01-01
  • 2012-07-16
  • 1970-01-01
  • 1970-01-01
  • 2013-03-21
  • 2012-01-22
  • 2015-02-28
相关资源
最近更新 更多