За исключением:
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), когда говорим о повторных циклах.