Blog

Mastering `this` in JavaScript

JavaScript developers—new and experienced—constantly rate the variable this as a major pain of the language. The this variable, which represents a function’s context, plays an important role in everyday JavaScript programming, so understanding how it works is critical. This blog post gives an overview of this and its many quirks and forms. We will lay out the four ways to set a function’s this value, as well as discuss some common use cases and pitfalls.

What is Context?

But first, what is context in the first place? Think of it like this: if a function is a sentence, the context is the sentence’s subject. It represents who or what the function is about. If a function is creating a new user, the context would be the user instance. The way we access a function’s context is through the variable this.

It seems simple, so why is it so hard to understand? Developers routinely get tripped up by this, and that’s largely due to the fact that we’re used to lexical scoping. With lexical scoping, we can see the value of a variable by finding its assignment in earlier code. So when we want to know whatusersCount holds, we can just scroll up and find where that variable is set.

Here’s a visual example:

js-this-0 

That’s lexical scoping.

The this variable, however, uses dynamic scoping. We have much less exposure to dynamic scoping than we do to lexical scoping, so naturally it’s more difficult to grasp. With dynamic scoping, variables are set depending on how the host function is called. So the value of this is any given function depends entirely on how and where that function was called in the first place.

Intuition and this

Before getting into the nitty gritty, let’s consider what we already know about this. Most anyone who’s written jQuery code has seen an event binding like this:

$('button').on('click', function() {
    $(this).addClass('was_clicked');
    // $(this) refers to the button the was clicked 
});

When that button is clicked, the callback function runs. We access the $(‘button’) that was clicked inside the callback with $(this). It makes intuitive sense that this would represent the button, because that is the natural subject of the callback function.

The Four Ways to Set a Function’s this Value

Without further ado, here are the 4 ways to set a function’s this value:

  1. Call a method on an object, like object.method()
  2. Pass this in with .call() and .apply()
  3. Use new to make this start out empty
  4. Manually bind a this value with .bind()

If none of the above are used, this is the global scope object (i.e. window in a browser). This is avery bad thing and we should always avoid it by using one of the four rules.

Now, let’s dive into each method.

CALL A METHOD ON AN OBJECT (LIKE OBJECT.METHOD())

 

js-this-1

When you call a method on an object, the value of this inside that method will be the object it was called on. So calling user.getName() will set getName’s this value to user. This can be tricky because you don’t actively set the value of this, but it gets populated nonetheless.

PASS THIS IN WITH .CALL() AND .APPLY()

Let’s talk first about .call(). In JavaScript functions are just objects, and they can have properties and methods like any other object. One of the methods built into every function is .call(). This accepts an argument for the host function’s this value. .call() executes your host function, setting its this value to whatever argument you supplied:

js-this-2

The near-identical twin of .call() is .apply(). The only difference here is how you pass arguments to the underlying function. From the example above, imagine that user.alertName()accepts an argument. With .apply(), all arguments are passed in inside a single array. With .call(), the arguments are passed in as comma-separated values.

js-this-3

The difference between .call() and .apply() are small, but it’s good to know they are there.

USE NEW TO MAKE THIS START OUT EMPTY

If you want your function to start out with a blank slate, you can set this as an empty object by calling that function using new. Then, inside the function, you can build up that object however you like by attaching values to this.

Functions invoked using new automatically return this:

function User(handle) {
    this.handle = handle;
}
var andrew = new User("@AndrewWK");
// andrew === {handle: "AndrewWK"}

MANUALLY BIND A THIS VALUE WITH .BIND()

The .bind() function is much like .call() in that you pass in your this value as the first argument to each function. The difference is that .bind() returns a function with your this value set, whereas .call() executes a function with your this value set. Calling .bind() does not run your function, it simply sets up a function with the correct context.

var alertName = function() {
    alert(this.handle);
}
var boundAlertName = alertName.bind({handle: "@AndrewWK"});
boundAlertName()
// alerts "@AndrewWK"

This is useful anywhere you use callback functions. By their very nature, callback functions must be passed around without executing immediately. But setting a this value with .call() also executes that function, defeating the purpose of a callback.

Managing this in a callback

And that’s it: those are the four ways to set a function’s this value. Those four rules are not too hard to remember, but there is a common pitfall you should know about. I’m talking about callbacks. It’s easy to be writing a callback and not even know it, like in setTimeout:

setTimeout(function() {
    $(button).addClass(red);
}, 1000); 

The setTimeout function accepts a callback, but since it’s not using one of the four rules for setting context, this defaults to the global window object. That’s fine in the example above, but becomes a bug in code like this:

$('button').on('click', function() {
    setTimeout(function() {
        // Uh oh! `this` is the global object!
        $(this).addClass('clicked');
    }, 1000); 
});

We’re expecting $(this) to refer to the button that was clicked, but it doesn’t, since this defaults to the global window object. One way to solve this issue is to store our desired value of this in a local variable and then simply use that variable in a child scope:

$('button').on('click', function() {
    var _this = this;
    setTimeout(function() {
        $(_this).addClass('clicked'); // All better
    }, 1000); 
});

Of course there are many ways to accomplish the same thing. You could use .bind(), .call(), or a number of other options. Choose what works best for each individual situation.

In Review

The this variable is a critical tool that enables important concepts in JavaScript programming: modules, browser-based MVCs, functional programming methods, and more.

Though it may feel hard to master, the four rules listed above are the only ways to assign the value of this, and remembering them will make you a powerful JavaScript developer.