Sunday, July 11, 2010

Javascript functions - Part 3

In this post, I am going to experiment with a few more ways of invoking functions in javascript. In my previous post Javascript functions - Part 2 I elaborated on a few ways in which functions can be called.

Unlike many other languages, javascript functions are more than just functions. They are objects as well. And since functions, being an object, can have more functions defined for them, javascript allows us to have two pretty useful functions that can be used to call functions.

Every function in javascript can be called by its name and passing parameters. However, the context in which a function is being called can be different everytime.

What? Context? Now where did that come into the picture?

Well, if i were to define the context, I'd say that its nothing but the value of the 'this' in the function that is being executed. Seems pretty simple eh?

But somehow, the value of 'this' is not as easily grasped while programming in javascript. This is because 'this' is not your usual 'this' that you see in other languages like Java. For someone like me who has been meddling with hard code oops concepts, the way in which 'this' was being used in the javascript was pretty elusive in the beginning.

I am not going to be talking much about 'this' in today's post, however, there is something that we need to know before we can check out the the 2 new ways in which functions can be invoked in javascript.

'this' is something that you must know! < insert wicked laughter here >

Whenever a function is being invoked in javascript, and there is no object who is the invoker of the function, the window object is considered to be the default invoker.
Also, when defining objects that are not members of other objects, the objects are considered to be members of the default object - the window object.

Well, the above two lines are pretty simple. But lets see how these simple little things can make the behavior of code unpredictable when programming if you are not careful with your code.

Lets assume we have the following function defined in our script

var v={a:10};
  v.fun4=function(){
   alert("fun4 "+ this);
   fun5();
  }

function fun5(){
   alert("fun5" + this);
  }

v.fun4();


What I have done here is pretty simple. I created an object 'v' with a property 'a' and a function fun4(). fun4() alerts a message and then calls fun5(). Run this code and see what happens. As you see, the first alert indicates that the value of the 'this' keyword in the function fun4 is the object(in our case it is 'v'. You can check it by printing the value of v.a). But the second alert indicates that the value of 'this' is not the object v, but instead it is the default window object.

If you are not familiar with javascript, you might be perplexed, maybe just for a while, before the concept actually begins to reveal itself.

As you see, during the declaration of the function fun5(), it is not declared as a function of any object. Hence, this function is implicitly a function of the window object. Functions declared on the window object are global and can be called by any other function, which is exactly what our fun4() does. Hence, the default context of the function fun5() is the window object, which is the value of our 'this' keyword.

But what if you want the value of 'this' to be the object 'v' in fun5? What do you do?
Well, if you are thinking that you can simply replace the line fun5() with this.fun5(), that, my friend, is simply not gonna work. That's because, the function fun5() is not a function of the 'this' object in fun4().


However, there is a way to do it. The call() and apply() functions in javascript can be used to aid us in exactly this sort of scenario. these functions are used to set the context of the function, which is thereby reflected in the value of the 'this' keyword of the function.

In the following piece of code, we are going to declare 2 more functions on our object 'v'. And 2 global functions on the window object.

v.fun6() invokes global function fun7() using the call() function.

v.fun8() invokes global function fun9() using the apply() function.

v.fun6=function(){
   alert("fun6 "+ this);
   fun7.call(this,'ryan');
  }
  
  function fun7(arg){
   alert("fun7" + this + " arg : "+ arg);
  }
  
  v.fun8=function(){
   alert("fun8 "+ this);
   fun9.apply(this,['ryan']);
  }
  
  function fun9(arg){
   alert("fun9" + this + " arg : "+ arg);
  }
  
  v.fun6();
  v.fun8();


As it can be seen, the global functions also receive a parameter. The syntax of call() and apply() are quite similar. The first argument of both these functions is used as the context in which the actual function is to be called.

The call() method can have many arguments after the first argument such that each argument becomes the argument to be passed to the actual function.

The second argument of the apply() function takes an array of values that are to be passed as arguments to the actual function.

Run the code and see for yourself, how the value of the 'this' keyword is now appropriately being used in the global functions.

One implication of this sort of technique is that you can have a number of global functions, each of which does a single common task. And you can have several objects, belonging to different classes that call these global function using their own context, thereby reducing the redundancy of existing functionality.

That's all for now. We shall do some more experiments in the next post. :>

Happy Programming :)
Signing Off
Ryan

No comments: