【问题标题】:JS: using 'var me = this' to reference an object instead of using a global arrayJS:使用 'var me = this' 来引用对象而不是使用全局数组
【发布时间】:2010-03-14 16:36:13
【问题描述】:

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

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

首先看一下代码:

<!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 上造成内存泄漏或可能导致浏览器运行缓慢或崩溃的奇怪循环引用?

我不知道如何解释,但来自 C/C++ 编码员的背景,以第二种方式执行听起来像是某种循环引用,可能会在某些时候破坏内存。

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

谢谢

【问题讨论】:

    标签: javascript memory-leaks closures


    【解决方案1】:

    旁白:

    var HelloObject = 函数 HelloObject(div_container)

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

    您认为第二个选项是否会在 IE 上造成内存泄漏

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

    你应用到点击事件的监听器有一个闭包,保持对父函数作用域的引用。在这两个示例中,div_container 对象都在该范围内,因此您有一个循环引用:

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

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

    要阻止这种情况发生,您可以将 click 函数定义从父级中拉出:

    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
    

    你真的关心这种循环吗?也许不是。它只真正影响 IE6-7;这在 IE6 中很糟糕,因为在您退出浏览器之前不会归还内存,但随后有越来越多的观点认为,任何仍在使用 IE6 的人都应该得到他们所获得的一切。

    在 IE7 上,内存泄漏会堆积起来,直到您离开页面,所以它只对您丢弃旧的 HelloObject 并反复绑定新的非常长时间运行的页面很重要。 (在这种情况下,不丢弃旧值的 HelloObjects 线性数组本身在页面卸载之前也是内存泄漏。)

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

    一些框架有自己的用于事件的解耦。例如,jQuery 将事件处理函数附加到数据查找中,由它放入每个 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);
    };
    

    这可能会导致浏览器变慢或崩溃???

    不,我们在谈论 refloop 时真正担心的只是内存泄漏(然后主要是在 IE 中)。

    【讨论】:

    • +1 感谢您提供详细且非常有用的解释。
    【解决方案2】:

    答案在问题的正文中。由于您的 C++ 背景,您觉得这很奇怪。第二个选项是 Javascript-ish 的方式。

    【讨论】:

      猜你喜欢
      • 2012-04-24
      • 1970-01-01
      • 2015-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-08
      相关资源
      最近更新 更多