DJ Quilter

Front-End Developer

Context in Javascript: Call, Bind and Apply

Understanding context and how it can change in a piece of Javascript is an important part of writing more complicated applications. Context in Javascript is the value that you get from the this keyword. It will change as you move through an application, sometimes referring the the global window, sometimes to particular Objects, sometimes to Events (and much more).

The bind, call and apply methods are all useful for making sure that context is exactly what you expect it to be, but I’ve often found it difficult to find a succinct explanation of the differences between these methods.

The following post explains the differences with some code examples, but if you just want some simple definitions, you could skip to the end.

Bind

My simplistic definition of the bind method is as follows:

The bind method sets the context of a function without actually calling that function.

Lets look at an example

var Jedi = {
    doForceStuff: false,
    useForce: function() {
        console.log(this); // Ref to Jedi
        if(this.doForceStuff) {
            console.log('used force');
        }
    },
    discoverTalent: function() {
        console.log(this); // Ref to Jedi
        var test = window.setTimeout(function() {
            console.log(this); // Ref to window
            this.doForceStuff = true; // Errors, wrong context!
        }, 1000);
    }
}

var rey = Object.create(Jedi);
rey.discoverTalent();

We have a Jedi Object with two functions; the context for both of these functions is the Jedi Object. The discoverTalent function sets a variable on the Object which allows it to useForce (after a delay). However within the discoverTalent function, there is another function within a setTimeout whose context is the window Object. This, at the moment, is preventing the doForceStuff variable from being set to true.

How do we fix this? With the bind method!

var Jedi = {
  ...
    discoverTalent: function() {
        console.log(this); // Ref to Jedi
        var test = window.setTimeout(function() {
            console.log(this); // Ref to Jedi
            this.doForceStuff = true; // Sets the correct variable
        }.bind(this), 1000);
    }
}

Here, the discoverTalent function has been changed by the addition of the bind method to the end of the the offending function (the anonymous one inside the setTimeout). The context that we want to use is passed as a parameter of the bind.

The bind method, for me, seems to be most useful for things like setTimeout, setInterval or event handlers. In these instances, we’re setting up a function before something happens, and we know that the context within that function is not going to be the one that we want. The bind methods lets us take action to make sure that it is exactly what we need it to be.

Call

Sometimes, we need to set the context at the point when a function is invoked. For example, we might have a function that could be passed multiple variations of the same context. This is where call comes in handy. Here’s another simplistic definition for you:

The call method allows you to set the context of a function that you are calling. Other parameters that the function requires are passed separately after the reference to the new context.

That was a bit of a mouthful.

The main difference between call and bind are whether you’re running the function straightaway (call), or just assigning the context for posterity (bind).

This next example shows how you might use call:

var Jedi = {
    ...
    findLightsaber: function(color, previousOwner) {
        this.lightsaber = Object.create(Lightsaber).init(color, previousOwner);
    }
};

var Lightsaber = {
    colour: null,
    previousOwner: null,
    init: function(color, prevOwner) {
        this.color = color;
        function setPreviousOwners(prev) {
            console.log(this); // Refers to window
            this.previousOwner = prev; // Error
        }
        setPreviousOwners(prevOwner);
    }
};

var rey = Object.create(Jedi);
rey.findLightsaber(‘blue’, ‘luke’);

In the above example, we’ve added a new function to the Jedi Object so that they can find a lightsaber. This creates a new Lightsaber Object, which has an init function that is called immediately within the Jedi’s findLightsaber function. This sets the colour of the blade and an owner history.

However. The history of previous owners can (and will) get complicated, so we’ve created a new function inside of init to deal with that complication. This inner function does not inherit the Object context from its parent function (init) which results in an error.

Now we could fix this by passing the context as a parameter of setPreviousOwners. But, we can also use the call method, like so:

var Lightsaber = {
    ...
    init: function(color, prevOwner) {
        this.color = color;
        function setPreviousOwners(prev) {
            console.log(this); // Refers to Lightsaber
            this.previousOwner.push(prev); // Sets one previous owner
        }
        setPreviousOwners.call(this, prevOwner);
    }
};

var rey = Object.create(Jedi);
rey.findLightsaber(‘blue’, ‘luke’);

Once again, we’ve changed one line, adding the call method when we invoked the setPreviousOwners function. The context is passed as the first parameter, and then the normal parameters (that the function is expecting) are passed.

And everyone lived happily ever after.

Apply

Or did they?!

What if there are lots of previous owners. Say strange old hermits or absent fathers? Our setPreviousOwners function is going to be getting new parameters that it doesn’t expect. And can never guess at how many it’s going to get.

This is where the apply method is useful:

The apply method allows you to set the context of a function that you are calling. Other parameters that the function requires are passed in an array after the reference to the new context.

Now our code starts looking a little more complicated:

var Lightsaber = {
    ...  
    previousOwners: [],
    init: function() {
        var owners = arguments;
        function setPreviousOwners(args) {
            console.log(this); // Refers to Lightsaber
            for (i = 0; i < args.length; i++) {
                if (i === 0) {
                    this.color = args[i];
                } else {
                    this.previousOwner.push(args[i]);
                }
            }
        }
        setPreviousOwners.apply(this, owners);
    }
};

var rey = Object.create(Jedi);
rey.findLightsaber([‘blue’, ‘luke’, ‘ben’, ‘anakin’]);

Here we’ve edited the previous version to include all of the parameters in an array which is then parsed by the setPreviousOwners function. The colour goes to the variable it’s supposed to go to, and each of the previous owners are pushed into their variable, which is now an array.

I’ve found call and apply to be particularly useful when working with nested functions whose context changes as you get deeper. As I already mentioned, they’re are also useful when creating lots of instances of the same Object.

The Quick Definitions

So, those definitions again are:

The bind method sets the context of a function without actually calling that function.

The call method allows you to set the context of a function that you are calling. Other parameters that the function requires are passed separately after the reference to the new context.

The apply method allows you to set the context of a function that you are calling. Other parameters that the function requires are passed in an array after the reference to the new context.

When you put it that way, it all makes sense.

The Next Level: ES2015 and Arrow Functions

The future of Javascript has a new way of passing context in the arrow function. Currently browser support is not great, but it’s something that’s worth knowing. MDN describes an arrow function in the following way:

Arrow functions capture the this value of the enclosing context...

That is, an arrow function takes the context of its container.

Before showing how arrow functions apply to our Jedi example, it’s worth seeing they ways in which you would write one:

var funcOne = a => a;
var funcTwo = (a, b) => { return a * b };
var funcThree = () => 1;

console.log(funcOne(2)); // Returns 2
console.log(funcTwo(2, 2)); // Returns 4
console.log(funcThree()); // Returns 1

You’ll notice that the version with the curly braces includes a return. The version without the curly braces doesn’t need this, always assuming that the value is to be returned.

Parameters are defined before the arrow (=>) and need to be surrounded by braces unless there is only one parameter.

So how does this help our Jedi? Going back to the discoverTalent function:

var Jedi = {
    ...
    discoverTalent: function() {
        console.log(this); // Ref to Jedi
        var test = window.setTimeout( () => {
            console.log(this); // Ref to Jedi
            this.doForceStuff = true; // Sets the correct variable
        }, 1000);
    }
}

The difference in the new function here is that the anonymous function within the setTimeout is now created using the new arrow function.

Conclusion

The bind, call and apply methods are all powerful bits of code. Once you know how to use them. Happy context changing!