JS:使用'var me = this'来引用对象而不是使用全局数组

时间:2010-03-14 16:36:13

标签: javascript memory-leaks closures

下面的例子只是一个例子,我知道当用户点击div块时我不需要一个对象来显示警告框,但这只是一个简单的例子来解释编写JS时经常发生的情况代码。

在下面的示例中,我使用全局可见的对象数组来保持对每个新创建的HelloObject 的引用,这样在单击div块时调用的事件可以使用arry中的引用调用HelloObject的公共函数hello()。

第1看看代码:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html><head><meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
   <title>Test </title>   
   <script type="text/javascript">      
      /*****************************************************
      Just a cross browser append event function, don't need to understand this one to answer my question
      *****************************************************/
      function AppendEvent(html_element, event_name, event_function) {if(html_element) {if(html_element.attachEvent) html_element.attachEvent("on" + event_name, event_function); else if(html_element.addEventListener) html_element.addEventListener(event_name, event_function, false); }}

      /******************************************************
      Just a test object
      ******************************************************/      
      var helloobjs = [];
      var HelloObject = function HelloObject(div_container)
      {         
         //Adding this object in helloobjs array
         var id = helloobjs.length; helloobjs[id] = this;

         //Appending click event to show the hello window 
         AppendEvent(div_container, 'click', function()  
         {              
            helloobjs[id].hello(); //THIS WORKS! 
         }); 

         /***************************************************/
         this.hello = function() { alert('hello'); }
      }      
   </script>
</head><body>
   <div id="one">click me</div>
   <div id="two">click me</div>      
   <script type="text/javascript">
      var t = new HelloObject(document.getElementById('one'));
      var t = new HelloObject(document.getElementById('two'));
   </script>
</body></html>

为了获得相同的结果,我可以简单地替换代码

     //Appending click event to show the hello window
     AppendEvent(div_container, 'click', function() 
     {             
        helloobjs[id].hello(); //THIS WORKS!
     });

使用此代码:

     //Appending click event to show the hello window
     var me = this;
     AppendEvent(div_container, 'click', function() 
     {             
        me.hello(); //THIS WORKS TOO AND THE GLOBAL helloobjs ARRAY BECOMES SUPEFLOUS!
     });

因此会使 helloobjs数组超级丰富。

我的问题是:你认为第二个选项是否会在IE上创建memoy泄漏或奇怪的特定引用可能会导致浏览器变慢或中断?

我不知道如何解释,但是作为C / C ++编码器来自背景,以第二种方式进行听起来像是某种循环引用,可能在某些时候破坏记忆。

我还在互联网上阅读了关于IE闭包内存泄漏问题http://jibbering.com/faq/faq_notes/closures.html(我不知道它是否在IE7中得到修复,如果是,我希望它不会在IE8中再次出现)。

由于

2 个答案:

答案 0 :(得分:2)

除了:

  

var HelloObject = function HelloObject(div_container)

通常尽量不要使用命名内联函数表达式。它通常不会让你得到任何东西,并且它们在JScript(IE)中存在严重问题。使用function HelloObject() { ... }语句或var HelloObject= function() { ... };匿名表达式。

  

你认为第二个选项会在IE上创建memoy泄漏

“创建”?不,您现有的代码已经在IE上泄露了。

您应用于click事件的侦听器具有闭包,保留对父函数作用域的引用。在这两个示例中,div_container对象都在该范围内,因此您有一个循环引用:

div -> onclick ->
listener function -> parent scope ->
parent function -> reference to div

包含本机JS对象和宿主对象(例如div,DOM节点)混合的引用循环是导致IE6-7内存泄漏的原因。

要阻止这种情况发生,您可以将点击功能定义拉出父项:

function HelloObject(div_container) {
    AppendEvent(div_container, 'click', HelloObject_hello);
}
function HelloObject_hello() {
    alert('hello');
}

现在没有关闭,div_container没有被记住,并且没有循环/泄漏。但是,hello函数只是一个函数,不知道它属于哪个div_container(除了查看它点击的event / this之外)。

更典型的是,你需要记住div,或者,如果你是以对象的方式做事,this

function HelloObject(element) {
    this.element= element;
    AppendEvent(element, 'click', this.hello.bind(this));
}
HelloObject.prototype.hello= function() {
    alert('Hello, you clicked on a '+this.element);
};

About function.bind。)

当然会带回参考循环:

element -> onclick
bound hello function -> bound this
Helloer instance -> 'element' member
reference to element

你真的关心这种refloop吗?也许不是。它只影响IE6-7;它在IE6中很糟糕,因为在您退出浏览器之前内存没有被回馈,但是随后有一种越来越多的想法认为仍然使用IE6的人应该得到他们得到的一切。

在IE7上,内存泄漏会一直存在,直到你离开页面为止,所以它只对你长时间运行的页面很重要,你会丢弃旧的HelloObject并反复绑定新的HelloObjects。 (在这种情况下,一个不丢弃旧值的HelloObjects线性数组本身也会出现内存泄漏,直到页面卸载为止。)

如果你真的在乎,因为你正在为一些运行IE6而且没有人关闭浏览器的肮脏公司工作,那么(a)我的哀悼,(b)是的,你确实必须实施像您拥有的查找对象,充当DOM节点和您用作事件侦听器的Function对象之间的解耦层。

某些框架有自己的事件解耦。例如,jQuery将事件处理函数附加到数据查找,由一个id放入每个Element对象的id;这使得它可以免费解耦,尽管它确实存在问题......

如果您使用的是纯JavaScript,这里有一些示例帮助程序代码。

// Event binding with IE compatibility, and decoupling layer to prevent IE6-7
// memory leaks
//
// Assumes flag IE67 pre-set through eg. conditional comments
//
function EventTarget_addEventListener(target, event, listener) {
    if ('addEventListener' in target) {
        target.addEventListener(event, listener, false);
    } else if ('attachEvent' in target) {
        if (IE67)
            listener= listener.decouple();
        target.attachEvent('on'+event, listener);
    }
}

Function.decouple_bucket= {};
Function.decouple_factory= function() {
    function decoupled() {
        return Function.decouple_bucket[decoupled.decouple_id].apply(this, arguments);
    }
    return decoupled;
};
Function.prototype.decouple_id= null;

Function.prototype.decouple= function() {
    var decoupled= Function.decouple_factory();
    do {
        var id= Math.floor(Math.random()*(Math.pow(2, 40)));
    } while (id in Function.decouple_bucket);
    decoupled.decouple_id= id;
    Function.decouple_bucket[id]= this;
    return decoupled;
};
Function.prototype.release= function() {
    delete _decouple_bucket[this.decouple_id];
};
if (IE67) {
    EventTarget_addEventListener(window, 'unload', function() {
        Function.decouple_bucket.length= 0;
    });
}

// Add ECMA262-5 method binding if not supported natively
//
if (!('bind' in Function.prototype)) {
    Function.prototype.bind= function(owner) {
        var that= this;
        if (arguments.length<=1) {
            return function() {
                return that.apply(owner, arguments);
            };
        } else {
            var args= Array.prototype.slice.call(arguments, 1);
            return function() {
                return that.apply(owner, arguments.length===0? args : args.concat(Array.prototype.slice.call(arguments)));
            };
        }
    };
}

在完成所有繁琐的管道之后,您可以说:

function HelloObject(element) {
    this.element= element;
    EventTarget_addEventListener(element, 'click', this.hello.bind(this));
}
HelloObject.prototype.hello= function() {
    alert('Hello, you clicked on a '+this.element);
};
  

可能会导致浏览器变慢或中断???

不,在谈论refloops时,我们只是担心内存泄漏(主要是在IE中)。

答案 1 :(得分:0)

答案在于问题的正文。由于你的C ++背景,你似乎很奇怪。第二个选项是Javascript-ish的方式。