JS: использование 'var me = this' для ссылки на объект вместо использования глобального массива - PullRequest
0 голосов
/ 14 марта 2010

Пример ниже, это просто пример, я знаю, что мне не нужен объект для отображения окна предупреждения, когда пользователь нажимает на блоки div, но это просто простой пример, чтобы объяснить ситуацию, которая часто происходит при написании JS код.

В приведенном ниже примере я использую глобально видимый массив объектов, чтобы сохранить ссылку на каждый новый созданный HelloObject , таким образом, события, вызываемые при нажатии на блок div, могут использовать ссылку в массиве для вызовите публичную функцию 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 или странные специфические ссылки, которые могут привести к замедлению работы браузера или его поломке ???

Я не знаю, как объяснить, но, исходя из фона в качестве C / C ++ -кодера, выполнение этого 2-го способа звучит как некая круговая ссылка, которая может в какой-то момент сломать память.

Я также читал в Интернете о проблеме утечки памяти при закрытии IE http://jibbering.com/faq/faq_notes/closures.html (я не знаю, было ли это исправлено в IE7, и если да, я надеюсь, что это не выйдет снова в IE8).

Спасибо

Ответы [ 2 ]

2 голосов
/ 14 марта 2010

За исключением:

var HelloObject = функция HelloObject (div_container)

В общем, старайтесь не использовать именованные выражения встроенных функций. Обычно это ничего не дает, и с ними в JScript (IE) возникают серьезные проблемы. Либо используйте оператор function HelloObject() { ... }, либо var HelloObject= function() { ... }; анонимное выражение.

делает этот 2-й вариант, по вашему мнению, причиной утечки памяти в IE

«Создать»? Нет, ваш существующий код уже имел утечки в 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);
};

( О функции.bind .)

Что, конечно, возвращает референсный цикл:

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

Тебя действительно волнует этот тип рефлопов? Может и нет. Это действительно влияет только на IE6-7; Это плохо в IE6, так как память не возвращается до тех пор, пока вы не выйдете из браузера, но есть растущая школа мысли, которая говорит, что любой, все еще использующий IE6, заслуживает всего, что он получает.

В IE7 утечки памяти накапливаются до тех пор, пока вы не покинете страницу, поэтому это имеет значение только для очень долго работающих страниц, где вы отбрасываете старые HelloObjects и повторно связываете новые. (В этом случае линейный массив объектов HelloObject, который не отбрасывает старые значения, также будет утечкой памяти до тех пор, пока страница не будет выгружена сама по себе.)

Если вам все равно, потому что вы работаете на какую-то грязную компанию, которая работает с IE6, и никто никогда не закрывает их браузеры, тогда (а) мои соболезнования и (б) да, вам действительно придется реализовать что-то вроде объект поиска, который вы использовали, чтобы действовать как разделительный слой между узлами DOM и объектом Function, который вы используете в качестве прослушивателя событий.

Некоторые фреймворки имеют свою собственную развязку, используемую для событий. Например, jQuery присоединяет функции-обработчики событий к поиску данных, индексируемому идентификатором, который он помещает в каждый объект Element; это дает ему возможность разъединения бесплатно, хотя у него есть собственные проблемы ...

Если вы используете простой 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);
};

, что может привести к замедлению работы браузера или его поломке ???

Нет, мы беспокоимся только об утечках памяти (и в большинстве случаев в IE), когда говорим о повторных циклах.

0 голосов
/ 14 марта 2010

Ответ находится в теле вопроса. Это кажется вам странным из-за вашего опыта в C ++. Второй вариант - это способ сделать это с помощью Javascript.

...