检测JavaScript中的内存泄漏

时间:2009-12-09 05:42:25

标签: javascript memory-leaks

我有这个代码,我在我为项目构建的一些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();

1 个答案:

答案 0 :(得分:16)

内存消耗

这种方法的问题在于,所有方法的代码的全部将被复制到每个实例,这可能会变成一个大的内存消耗(除非执行上下文有很好的优化)你不能依赖)。你所做的就是点击处理程序直接调用你想要它调用的方法。

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

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

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

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和衍生产品),当你完成它时(例如,离开页面时)解开你的事件处理程序是很重要的。因此,如果您稍后不清除它们,则两个onclick分配都可能是某些浏览器的内存泄漏。这是因为所涉及的浏览器不处理DOM元素和JavaScript对象之间的循环引用,当它们都没有被引用时(例如,它们相互引用但没有其他任何引用它们)。您必须断开DOM元素和JavaScript函数之间的链接,以确保清除它们。如果你使用他们的方法来附加事件处理程序,那么像Prototype这样的Libary会在你卸载页面时为你做这件事。我不太了解其他的文库,无法评论他们是否这样做。

其他注释

有点OT,但是:

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