OOP in JavaScript - Part 4: Scoping

Permanent Link: OOP in JavaScript - Part 4: Scoping 21. Dezember 2009 RSS Feed for comments on RSS-Feed für Kommentare zu: OOP in JavaScript - Part 4: Scoping comments feed

I agree, it took me a while to write part 4 of my OOP in JavaScript series, but here it is now. As for the whole series, basic JavaScript knowledge is needed.

Scoping together with OOP is always an issue, when using asynchronous calls combined with callbacks. Let's create a very easy example where one method of a class registers an onclick EventListener and the second method of the class is the callback for the EventListener. The second method simply calls an alert with a message text defined in the class. Note that the Event Listener registration is not cross-browser safe:

function Handler()
{
}

Handler.prototype.messageText = 'You have clicked somewhere in the document';

Handler.prototype.registerAll = function()
{
window.addEventListener('click', this.handleOnclick, true);
}

Handler.prototype.handleOnclick = function(event)
{
alert(this.messageText);
}

var handler = new Handler();
handler.registerAll();

When clicking anywhere in the document you'd be expecting to see a message box "You have clicked somewhere in the document", but you only get a mesage box saying "undefined". Why is that?

The second argument of addEventListener() is the callback, it expects a function. By putting this.handleOnclick as callback, you are passing a copy of that function. It would be same if you had written it like this:

window.addEventListener('click', function() { alert(this.messageText); }, true);

As you can clearly see now the function stands for itself, this is not in the context(=scope) of the class Handler. In order to achieve exactly that, JavaScript gives us 2 possibilities: apply() and call(). Basically they are both the same, the only difference is in the way you pass further arguments. For a closer distinction, take a look at this page.

By passing an instance as first argument we are telling the function that is called in which scope to be executed, meaning: What instance is this? Let's alter the registerAll method of the example:

Handler.prototype.registerAll = function()
{
window.addEventListener('click', this.handleOnclick.apply(this), true);
}

When running your page now, you will notice that the alert puts out the right message, but is called the moment your page is rendered and the onclick event doesn't work anymore. This is because apply() and call() directly call the respecting function.

What we need to do, is to dynamically create a function that is called with the right scope when needed.

function Handler()
{
}

Handler.prototype.messageText = 'You have clicked somewhere in the document';

Handler.prototype.registerAll = function()
{
window.addEventListener('click', this.createOnclickHandler(), true);
}

Handler.prototype.createOnclickHandler = function()
{
var myScope = this;
return function(event) {
var handle = function(event) {
alert(this.messageText);
}
handle.call(myScope, event);
}
}

var handler = new Handler();
handler.registerAll();

Finally we get to see a "You have clicked somewhere in the document" message box only when clicking somwhere in the document. Note that the second parameter of call() is the Event Object, which is passed as first argument to the called function.

If using this is not total must for you, you can also throw away one of the functions and simply use myScope instead of this:

Handler.prototype.createOnclickHandler = function()
{
var myScope = this;
return function(event) {
alert(myScope.messageText);
}
}

0 comments

No Comments yet.

Write a comment

(will not be published)