A common pattern that we see in review of Mozilla addons is code that looks like the following:
var myExtension = {
saveMe: null,
onLoad: function () {
this.saveMe = "I am saved";
}
}
window.addEventListener("load", myExtension.onLoad);
Unfortunately the value of “this.saveMe” that is used in onLoad is the value from the global window, not the value from the myExtension object. Really the event listener is looking for an object of type nsIDOMEventListener, with a method handleEvent. When it does not find that, it applies some magic and just uses the entered function. Unfortunately, in the process the “this” does not get defined from the parent object, and you are stuck with “this” being the global object.
Here are three alternatives that don’t leak:
1) give it what it wants, namely a handleEvent method:
var myExtension = {
saveMe: null,
handleEvent: function (evt) {
if (evt.type == "load")
this.saveMe = "I am saved";
}
}
window.addEventListener("load", myExtension);
2) wrap the function call with a function:
var myExtension = {
saveMe: null,
onLoad: function () {
this.saveMe = "I am saved";
}
}
window.addEventListener("load", function() { myExtension.onLoad(); });
3) use the bind function to manually specify what “this” will be used:
var myExtension = {
saveMe: null,
onLoad: function () {
this.saveMe = "I am saved";
}
}
window.addEventListener("load", myExtension.onLoad.bind(myExtension));
These kinds of issue can be difficult to spot just by looking at code. Addon authors are strongly encouraged to use the “Extension Test” addon to catch leaks of names to global namespaces.
Why wouldn’t you just do:
myExtension.saveMe = “I am saved”;
You can do that, in fact that is often the workaround that extension writers use when they don’t understand how to get the “this” working.
I wish there were tools like that for inexperienced Greasemonkey script writers like myself
Note that only method 1) allows you to conveniently remove the event listener (not an issue for some event listeners, such as unload).
Maybe I’m weirder than most, but I don’t use that method for my addons anyway. I do everything in an anonymous function:
(function() {
var saveMe;
function onLoad() {
saveMe = “I am saved”;
}
window.addEventListener(“load”, onLoad);
})();
That way I don’t put anything in the global namespace, I’m free to access everything as local variables, and I can add and remove listeners at will.