JavaScript call and apply

From Trephine

Jump to: navigation, search
« JavaScript loop closures Script onload piggybacking »

[subscribe] Recent blog entries

Live Demos

JavaScript call and apply

Based on some great feedback to my article about JavaScript's treatment of the this keyword, I've decided to have a stab at explaining two other often misunderstood JavaScript language features: call() and apply().

Note: If you're looking for something a bit shorter, check out the call and apply quick reference

Brief overview of JavaScript objects

In order to understand call() and apply(), you must first understand the role of functions and objects in JavaScript. In many popular languages objects are "first class citizens" in that you can assign them to variables - JavaScript is no exception. Consider this example:

var Bob = new Object();
Bob.name = "Bob";

In this case, the Bob variable has been assigned a new empty Object as its value. This object is then given a member called name with the value "Bob". The following code has exactly the same result as the previous, but uses JavaScript object notation rather than an explicit call to new Object():

var Bob = {
  name: "Bob"
};

Nothing terribly shocking about any of this - it should feel very familiar to just about everyone reading this article. If any of this is foreign to you, I recommend stopping now and reading more about JavaScript Objects.

JavaScript functions as variables

One really cool thing about JavaScript is that functions are also first class citizens. That is, a function can be assigned to a variable or member (property) of an object. Here's an extension of our first example, showing how you might do that:

var Bob = new Object();
Bob.name = "Bob";
Bob.greet = function() {
  alert("Hi, I'm " + this.name);
};

And here's the shorthand version using object notation:

var Bob = {
  name: "Bob",
  greet: function() {
    alert("Hi, I'm " + this.name);
  }
};

In the previous cases, we defined the values of Bob's members in line, but of course we could have created variables to hold them prior to assigning them as members. This example is logically equivalent to the previous two:

var bobsName = "Bob";
var bobsGreet = function() {
  alert("Hi, I'm " + this.name);
};
 
var Bob = new Object();
Bob.name = bobsName;
Bob.greet = bobsGreet;

The takeaway here is that an object's members may be either value/object types or function types.

What's the difference between a function and a method?

Having read up to this point, you would be forgiven for thinking that Bob.greet is a method in the traditional sense. It is not. A method is, by definition, bound to the object to which it is associated, but Bob's greet member is not bound - at least, not until it is called.

Consider this:

var genericGreet = function() {
  alert("Hi, I'm " + this.name);
};
 
var Bob = new Object();
Bob.name = "Bob";
Bob.greet = genericGreet;
 
var Alice = new Object();
Alice.name = "Alice";
Alice.greet = genericGreet;

What's the difference between Bob.greet and Alice.greet? The answer is "nothing" - they are both exactly the same as genericGreet, from which they were both assigned. That is, Bob.greet == Alice.greet == genericGreet.

So, what happens when we call Bob.greet() and Alice.greet()? In each case, the greet function is called with the respective object set to be this, and we get "Hi, I'm Bob" and "Hi, I'm Alice" respectively.

Understanding the above, it should be clear that the following code sets up Bob and Alice with exactly the same outcome, but without creating the intermediary genericGreet function:

var Bob = new Object();
Bob.name = "Bob";
Bob.greet = function() {
  alert("Hi, I'm " + this.name);
};
 
var Alice = new Object();
Alice.name = "Alice";
Alice.greet = Bob.greet;

Likewise, the following example is again the same, but written using object notation:

var Bob = {
  name: "Bob",
  greet: function() {
    alert("Hi, I'm " + this.name);
  }
}
 
var Alice = {
  name: "Alice",
  greet: Bob.greet
};

No matter which of the above you use, the result of Bob.greet() will be "Hi, I'm Bob", and the result of Alice.greet() will be "Hi, I'm Alice".

So when do I get to use call and apply?

Ok, now that all the groundwork is out of the way, it's time to get to the point. Suppose we want to call Bob's greet member function as Alice, but without setting Alice.greet. In code, the question looks like this:

var Bob = {
  name: "Bob",
  greet: function() {
    alert("Hi, I'm " + this.name);
  }
}
 
var Alice = {
  name: "Alice",
};
// How do we achieve the effect of Alice.greet() here, without modifying Alice?

This is where call and apply come into play. As mentioned before, functions in JavaScript are first class citizens. A JavaScript function is a kind of object, and in addition to being able to be assigned to variables, they also have their own member values and functions. For example, calling someFunction.toString() will return a JavaScript string representation of someFunction.

call and apply are members of the Function prototype, which means that you can call someFunction.call() or someFunction.apply() just as you'd call someFunction.toString().

To call Bob's greet method as Alice, the magical incantation is thus:

Bob.greet.call(Alice);
// -or-
Bob.greet.apply(Alice);

In our simple case, either of the above would produce the same result as if we had done this:

Alice.greet = Bob.greet;
Alice.greet();
delete Alice.greet;

Except that using call and apply prevent us from having to make any changes to Alice.

What's the difference between call and apply?

The only difference between call and apply is the manner in which additional arguments are passed. So far in our examples the greet function has taken no arguments, and so using call and apply is exactly the same. To demonstrate the difference, consider this revised greet implementation:

var friendlyGreet = function(somebody) {
  alert("Hi, " + somebody.name + ", I'm " + this.name);
};

The friendlyGreet function takes a single parameter, the person being greeted. Here are examples of Bob and Alice greeting each other using call():

friendlyGreet.call(Bob, Alice); // alerts "Hi, Alice, I'm Bob"
friendlyGreet.call(Alice, Bob); // alerts "Hi, Bob, I'm Alice"

And here are the same examples using apply():

friendlyGreet.apply(Bob, [ Alice ]); // alerts "Hi, Alice, I'm Bob"
friendlyGreet.apply(Alice, [ Bob ]); // alerts "Hi, Bob, I'm Alice"

Notice how the second argument in the apply examples are in brackets. This is shorthand JavaScript notation for creating an array with one element. While call() accepts an arbitrary number of arguments (the "this" object followed by any number of args), apply() takes exactly two: the "this" object and an array of arguments to pass.

In cases where the number of arguments to pass is known in advance (like friendlyGreet), using call makes sense, but when you have an unknown number of arguments, then apply is a better fit.

Hope this helps! As usual, I welcome all feedback and questions.

--Jim R. Wilson (jimbojw) 16:51, 20 March 2009 (UTC)
Personal tools