Finalizer is a general function designed to arrange that a function passed to it as an argument is executed during the onunload event in a browser. Its primary purpose is to address the memory leak problem on IE browsers but has been written to provide the facility cross-browser as it may have other applications.
The memory leak problem in IE is caused when DOM Elements hold references to JavaScript objects that simultaneously hold references to those DOM objects. This produces a circular reference a prevents IE's garbage collection routines from freeing either the DOM Elements or the JavaScript objects involved, leaving the memory that they occupy tied-up until the browser is closed. The effect can be significant with any sizeable DHTML such an drop-down menus, and accumulates during navigation around some DHTML rich sites.
The problem can easily be solved by freeing either references from DOM Elements to JavaScript objects, or from JavaScript objects to DOM Elements, or both, as the page is unloded.
Details of the IE memory leak problem, tests/demonstrations of the effect and other solutions could be found by searching the comp.lan.javascript archives on groups.google.com with the keywords "memory leak".
The script executes an anonymous function inline, returning a function
object that is assigned to the finalizeMe global variable. That
function is then called with code such as finalizeMe(functionRef);
, where functionRef is a reference to a function that will be called
onunload and is responsible for doing any reference breaking or clean-up
required. Any number of functions can be scheduled to execute onunload (up to
about 1000, which should be sufficient for most purposes). If the same function
is scheduled with finalizeMe more than once it will only be executed once
(duplicate function references are not added to the queue).
If it is necessary to remove a function that has been scheduled for
execution onunload then passing a reference to that function to
finalizeMe.remove(funcitonRef); will have that function
removed from the queue.
Because the script uses the inline execution of an anonymous function it must be imported before any code that uses it will be executed.
Some of the other example scripts use this function and can serve as examples of its use. The JS file version is "scripts/Finalizer.js".
var global = this;
var finalizeMe = function(){
var base;
var safe = false;
var svType = (global.addEventListener && 2)||
(global.attachEvent && 3)|| 0;
function addFnc(next, f){
function t(ev){
if(next)next(ev);
f(ev);
};
t.addItem = function(d){
if(f != d.getFunc()){ //don't add duplicates!
if(next){
next.addItem(d);
}else{
next = d;
}
}
return this;
};
t.remove = function(d){
if(f == d){
f = null;
return next;
}else if(next){
next = next.remove(d);
}
return this;
};
t.getFunc = function(){return f;};
t.finalize = function(){
if(next)next = next.finalize();
return (f = null);
};
return t;
};
function addFunction(f){
if(base){
base = base.addItem(addFnc(null, f));
}else{
base = addFnc(null, f);
}
};
function ulQue(f){
addFunction(f);
if(!safe){
switch(svType){
case 2:
global.addEventListener("unload", base, false);
safe = true;
break;
case 3:
global.attachEvent("onunload", base);
safe = true;
break;
default:
if(global.onunload != base){
if(global.onunload)addFunction(global.onunload);
global.onunload = base;
}
break;
}
}
};
ulQue.remove = function(f){
if(base)base.remove(f);
};
function finalize(){
if(base){
base.finalize();
switch(svType){
case 2:
global.removeEventListener("unload", base, false);
break;
case 3:
global.detachEvent("onunload", base);
break;
default:
global.onunload = null;
break;
}
base = null;
}
safe = false;
};
ulQue(finalize);
return ulQue;
}();