The .call(..) and .apply(..) functions

Two functions in Javascript that don't seem to get a whole lot of attention are the .call(..) and the .apply(..) functions. Both of these functions have a similar purpose; the major difference lies in how you plan to pass parameters. If you remember from the last blog entry, we examined briefly the .apply(..) function when we were creating a cross-browser solution for setTimeout(..).

In this entry, I want to focus more on the core purpose of these functions. This will eventually lead me into a future blog entry in which I talk about a solution for IE's lack of 'this' keyword support when using attachEvent(..).

But first we must understand more thoroughly what .call(..) and .apply(..) allow us to do. Read on to learn more.

Given a reference to a function, myFn, it is possible to invoke that function by calling:

myFn.call([someObj]);

At first glace, it doesn't seem like there is a whole lot to gain by doing this. After all, a simple call to myFn(); will also invoke the function.

The real magic happens when you realize that you can manually specify which object you want the function to treat as the current object. Take the following example:

function cat(name, color, age)
{
  this.name = name;
  this.color = color;
  this.age = age;
}

function frog(name, age)
{
  this.name = name;
  this.color = 'green';
  this.age = age;
}

function showAnimal()
{
  var str =
  this.constructor.name.toUpperCase() + '\n' +
  'name: ' + this.name + '\n' +
  'color: ' + this.color + '\n' +
  'age: ' + this.age;

  alert(str);
}

function init()
{
  var c = new cat('fluffy', 'white', 3);
  var f = new frog('igor', 2);
  showAnimal.call(c);
  showAnimal.call(f);
}

Here, we've defined two custom objects: cat and frog, as well as a function showAnimal(..) (notice that the showAnimal(..) function takes no parameters). Instead, showAnimal(..) uses the this keyword (which referes to the current object) to access various properties of the current object.

Now if you're anything like me, then the first time you took a look at this bit of code, your exact thought was proabably "… what?"

It's not that complicated really. Imagine if we had hooked up the showAnimal(..) function inside of the cat or frog class like this:

function cat(name, color, age)
{
  // name, color, age assignments here

  this.show = showAnimal;
}

The code snippet above should be pretty routine for most Javascript programmers. The statement that reads this.show = showAnimal; essentially defines a public function of the cat class which is just a reference to the showAnimal(..) function. Since this function is a member of the cat class, it has access to all of cat's internal member variables.

The issue here is that we'd have to hook up the .show(..) function for both the cat and the frog class. And if we decided to create more animals that had the properties name, color and age, then we'd have to hook up the .show(..) function for those classes as well.

Notice how our first code snippet alleviates the need to hook up the showAnimal(..) function individually for each class. By invoking the showAnimal(..) function via .call(animalRef), we tell the Javascript interpreter to treat animalRef as the current object (the object referred to by the this keyword).

If you wanted to pass additional parameters to the showAnimal(..) function, you could easily do that. Simply specify the paramaters you wish to pass in the same way you would any other function — only, do it after the first parameter, as in the following example:

  var myCat = new cat('Garfield', 'orange', 5);
  saySomethingToCat.call(myCat, 'Hello there', 'How are you?');

  function saySomethingToCat(msg1, msg2)
  {
    alert(msg1 + ' ' + this.name + '. ' + msg2);
  }

In this example, we passed in 2 (additional) parameters to the function saySomethingToCat(..). It's important to note that even those we're passing in 3 parameters (myCat, 'Hello there', and 'How are you?'), the first parameter, myCat, doesn't really count becuase it is this object that will be treated as the current object (and hence, the one referred to by the this keyword inside the saySomethingToCat(..) function).

The .apply(..) function works in almost exactly the same way, but with one minor difference (and if you read my last blog post, you probably already know what it is).

Instead of passing additional parameters as a comma delimited list as you would with the .call(..) function, you simply pass an array containing the data you want wish to pass as parameters. This can be very useful if the data you want to pass is already in an array (as it was when we were dealing with the arguments array in the last blog entry).

Rather than create a whole new example to demonstrate the .apply(..) function, I'm simply going to refer you to my previous blog entry regarding cross-browser setTimeout(..). I like to think it's one of the more brilliant Javascript functions I've come up with anyway, so I think everyone should read it :-)

As a final note, I like to say that I, personally, have found very few uses for the .call(..) and .apply(..) functions. One example is obviously the setTimeout(..) function in my last blog entry that I won't shut up about, and another is the ability to fix a this keyword reference in IE (but that is another blog entry — hopefully coming soon).

That doesn't mean there aren't more uses. It's just that, for almost every example I've seen using .call(..) and .apply(..), I can imagine other ways that I like better to accomplish the same thing. But if any readers out there have know of other useful way to take advantage of either of these functions, feel free to send me an email. I'd love to hear from you.

That about wraps up this entry. As always, comments welcome.

4 Responses

  1. siva charan Says:

    facing problems in preventing default action of onblur.here is my dotbt
    function loadEvents()
    {
    if(document.addEventListener)
    { document.getElementById("dSUpUName").addEventListener("blur",chkUserName,false);
    }
    else
    { document.getElementById("dSUpUName").attachEvent("onblur",chkUserName);
    }
    }
    function chkUserName(e)
    {
    this.focus();
    //return false;(first i tried like this to prevent default action)
    preventDefaultAction(e);
    }
    function preventDefaultAction(e)
    {
    e = e ? e : window.event;
    if(e.preventDefault)
    {
    e.preventDefault();
    }
    else
    {
    e.returnValue = false;
    }
    }
    </form
    how can i achieve this.i want to check that "dSUpUName" field with onblur.can you please help me regarding this

  2. sstchur Says:

    I don't quite understand what you're trying to do. The syntax you used for preventing the default action (e.preventDefault() and e.returnValue = false) is correct.

    But preventing the default action depends on the element and the event. For instance, if you have a link:

    <a href = "http://blog.stchur.com" rel="nofollow">stchur blog</a>

    And in a 'click' event handler for that link, you prevent the default action, you are telling the browser "don't do the think you normally do when a click occurs on a link." In other words, you're telling the browser NOT to navigate to the href of that link (which would be its default action).

    In your case, you are trying to prevent the default action of a 'blur' event on some element '#dSUpUName' (which I have no idea what type of element that is because you haven't told us). But in any case, I'm not sure that preventing the default action of a 'blur' is something the browser will let you do. I would also question if you really want to do it — it could make for a really lousy user experience.

    You may be able to re-focus #dSUpUName from within a 'focus' event handler of ANOTHER element. In that case, you might want to wait until the user gets all the way through the form and focuses on the submit button. At that point, perhaps you could validate the form and then call .focus() on the first invalid element you find.

  3. siva charan Says:

    thank you very much for your reply.i am sorry for providing insufficient data.what i am trying is which every one is doing now a days,it is checking username whether it is available to the user or not.here is my data.

    html page:

    User Name

    Password

    what i trying is chacking the username when user moves for the username field.for that i want to raise the onblur event handler with addEventListner or attachEvent,just like this.

    function loadEvents()
    {
    if(document.addEventListener)
    { document.getElementById("dSUpUName").addEventListener("blur",chkUserName,false);
    }
    else
    {
    document.getElementById("dSUpUName").attachEvent("onblur",chkUserName);
    }
    }
    function chkUserName(e)
    {
    //first i want to keep the foucs in the same element.for that i am using this.focus().
    this.focus();
    //return false;(first i tried like this to prevent default action)
    preventDefaultAction(e);
    }
    function preventDefaultAction(e)
    {
    e = e ? e : window.event;
    if(e.preventDefault)
    {
    e.preventDefault();
    }
    else
    {
    e.returnValue = false;
    }
    }

    and one more important thing is when we attach that event handler to the html tag it is working fine in all browsers

    for this the javascript code is

    function chkUserName()
    {
    document.getElementById('dSUpUName').focus();
    return false;
    }

    the problem occurs when we are trying to hadle the onblur event from javascript.
    and why the onblur event behaves so special when compared to other events.

    please help me…

  4. siva charan Says:

    one more thing is i found that if we are using setTimeout function like this

    function chkUserName()
    {
    stTimeout("document.getElementById('dSUpUName').focus()",100);
    }
    this is working fine.butwhy do we need that setTimeout function………

Got something to say?

Please note: Constructive criticism is welcome. Rude or vulgar comments however, are not and will be removed during moderation.