Monday, September 6, 2010

JavaScript concepts part 3 - callbacks and context

Introduction

JavaScript is a language that's widely used across the web, but it's often not deeply used by many of the page authors writing it. So in this "JavaScript concepts series" I try to introduce some of the deeper concepts of the JavaScript language. This is third article in series that bring you closer to understand how functions works.

Function callbacks

The nature of the code in a web pages is asynchronous, so it is nature of functions in JavaScript. And one of the most interesting concept in asynchronous programming are callback functions.

Consider example:

var myarray = [22, 21, 3, 5, 1, 105];

function sortAscending(a, b) {
return a - b;
}

myarray.sort(sortAscending);

alert(myarray);

The result of this function is alert with elements 22, 21, 3, 5, 1, 105 sorted ascending (1, 3, 5, 21, 22, 105).
So we passed function as reference inside of another function (sort). Passing a function as a parameter is no different in JavaScript than passing any other value. So, because sort function call back to function in our own code, this type of function constructions are termed as call back functions.

Usually are callback functions defined as anonymous functions because it look more elegant (sort of). As in the following example:

var myarray = [22, 21, 3, 5, 1, 105];

function sortAscending(a, b) {
return a - b;
}

myarray.sort(function (a, b) {
return a - b;
});

alert(myarray);

I personally like declared functions because you never really know if you are going to need it in some other place, and it also look less "hack-like" (I hate code that hides stuff from me!).
Functions context

OO languages provide a means (usually by using this) to reference the current instance of the object that we working on. JavaScript also posses this reference, but JavaScript implementation of this differs from its OO counterparts in subtle but significant ways.
In JavaScript, the context (this) of function is the object which contains some reference to invoke the function. This sound pretty confusing, but consider following example:

var desc = function() {
alert (this.address + "," + this.yearBuild);
}

var house = {
noOfdoors : 5,
address : "Main road 51",
yearBuild : new Date(2001, 2, 11),
description : desc
};

So in this example we have function that reference current context and show alert box. If we call this function like in following code:

house.description();

It will work fine, but if we call in on top-level context (window):

var desc = function() { //Top-level function declaration.
alert (this.address + "," + this.yearBuild);
}
desc(); //or window.description();

It will not work, to make it work we need to put variables on top-level context like so:

var desc = function() {
alert (this.address + "," + this.yearBuild);
}
address = 'Some road from Window';
yearBuild = new Date(2002, 3, 12);
desc();
description();

It will work because this is pointing to current execution context that is in this case top-level context.
So to wrap it up: In JavaScript the object referenced by this is determined not by how the function is declared but by how it's invoke (on which context is invoked to be precise). This means that the same function can have different context depending on how it's called.
As this is not enough, JavaScript gives us the means to explicitly control what's used as the function context. We can set the function context to whatever we want by invoking a function via the Funcion method call() or applay(). The call() method invokes the function specifying, as its first parameter the context that will be using, and the remainder of the parameters becomes the parameters of the called function. Second method (applay()) only use array of parameter and it will not be considered in following example because it behave exactly like call() function.
Consider the following code:

function showMe() {
return this.me;
}

var m1 = {
me : 'meFirst'
}
var m2 = {
me : 'meSecond'
}

window.me = 'meWindow'; //or me= 'meWindow'

m1.whoAmI = showMe;

alert(showMe()); //meWindow
alert(m1.whoAmI()); //meFirst, we change context to m1.
alert(showMe.call(m2)); //meSecond, explicitly changing context to m2.

I hope that this clear thing a little bit. In first alert we call function on top-context, simply by call it in alert function. In second alert we call it through referenced property on m1 object (so we call it on m1 object context). In third and last alert we explicitly change context to m2 and call it on m2 object context.

This is the end on part 3, please continue to part 4 (closures) if you like.

3 comments:

  1. Thanks for writing such a wonderful article for callback function in javascript. It makes clear my idea about callbakc fucntion.

    Shraddha Kulkarni.

    ReplyDelete
  2. Hi,
    Can you please explain execution of first example?

    Thanks,
    Shraddha Kulkarni

    ReplyDelete
  3. Hello, I did't not see your replay, sorry about that.

    In first example we have array of elements and function that will be passed as parameter to another function (sort in this context). "sort" function for arrays can sort array based on function we will provide (and pass like parameter). In Java we do this by implementing comparable interface. In this way we will "tell" sort function how to sort array element. And that's it, sort function "calls-back" to our custom function. The main point here is that second function "sort" call another function "sortAscending" after in its own context.

    ReplyDelete