Using the “call” and “apply” methods to change the context of a function in JavaScript

In JavaScript, the context that function is executing in is very important. What is context? Basically, the context of a function is what object it is “attached” to – this object will be the reference of the “this” keyword within that function. Every function has a context -that is to say, every function is attached to an object. By default it is the window object.

You may have run into this particular aspect of JavaScript when working with event handlers. For example:

function alertId()
{
  alert (this.id);
}

document.getElementById('someId').onclick = alertId;

This will return the “id” attribute of the element that the function has been attached to. You could attach the same function to multiple objects and each time you called it on a different object, it would return a different value, since the context is different. (Minor note: using element.attachEvent(), as part of the Microsoft event handler model, does not change the context of the function so “this” will still refer to the window object)

However, there may be times when you want to change the meaning of this without actually having to attach that a function to an object. Using the call() and apply() methods of the Function object can allow you to do this.

Context is everything

Let’s backtrack for a moment. In JavaScript, many (but not all) variables are objects. This includes the references to all functions as well – they are also objects. In the example above, using “alertId” (without the parentheses) would refer to the Function object that represents alertId. As the Function object is a predefined object in JavaScript, it has methods and properties associated with it.

Two of these methods are call() and apply(). Using these methods on a Function object allows you to set the context of that function – that is, what the “this” keyword refers to. The two only in how arguments are passed to the function whose context is being redefined. For example apply() takes an array of arguments like this:

function someFunction(arg1, arg2)
{
  this.id = arg1 + arg2;
}

var arrayOfArguments = ['valueOfArg1', 'valueOfArg2'];

someFunction.apply(objectToBeUsedAsThis, arrayOfArguments);

The values in the second-argument array are then used as the arguments to the someFunction() function.

The call() method is almost the same except it does not take an array of arguments, but rather just an indeterminate number of arguments that are all passed to the function. For example, the above could also be accomplished with this code:

function someFunction(arg1, arg2)
{
  this.id = arg1 + arg2;
}

someFunction.call(objectToBeUsedAsThis, 'valueOfArg1', 'valueOfArg2');

I prefer to use apply() sometimes since you can directly use the arguments variable that is local within all functions. (The variable arguments stores all the arguments that were passed to the function when it was called, and so this can be used in a situation with where it is desired to have a function with a variable number of arguments.)

A useful example

So far, the examples posed have been fairly trivial. Let’s look at a real-world use of call() to illustrate its usefulness. Remember the arguments variable? Basically, you have access to this object variable whenever you’re inside a function since it stores all the arguments passed to this function. However, it’s not exactly an array (although it works like one sometimes) and thus it does not have access to Array methods such as join().

We could easily convert it into an array by accessing each property by its index using a loop, but there’s an easier to way to do this. Consider the following code:

function someFunction()
{
  var argsArray = Array.prototype.slice.call(arguments, 0);
}

Using this one line, we have successfully converted the arguments into a true Array object and stored the result in the argsArray variable. How? First, we access the slice() method on the Array object through the prototype property and then call() the method telling it to use the arguments object as this.

The slice() method allows us to extract a section of an existing array. However, by using call() we are able to apply its effects on the arguments object that is similar to, but not exactly an Array object. The second argument of ‘0’ merely indicates that we want to extract not just a part of arguments but all of it. Since slice() always returns an Array object, we thus get back a true array containing the same data as the arguments object. And that’s it!

Note that the above example could be compacted even further. However, it looks very cryptic and can be confusing. (As if the previous example wasn’t confusing enough!)

function someFunction()
{
  var argsArray = [].slice.call(arguments);
}

This example works the same as the previous one. Instead of using Array.prototype, we create a ‘dummy’ empty array to get access to the slice() method. The second argument of ‘0’ isn’t needed, as if it is left out when using slice() the entire contents will be returned.

Contextual Conclusion

In short, using these methods to change the scope of a function can allow you to do some neat things with JavaScript that aren’t possible in some other languages. However, one should always be mindful to keep code readable and maintainable and to lessen the learning curve for those new to a language.

Comments for this entry are closed

But feel free to indulge in some introspective thought.