DEV Community

Cover image for 3 Techniques for Maintaining Your Sanity Using "This" in JavaScript
Carl Anderson
Carl Anderson

Posted on • Originally published at carlanderson.xyz on

3 Techniques for Maintaining Your Sanity Using "This" in JavaScript

Of JavaScript's many confusing aspects, the keyword this can be one of the most complicated -- Here's a joke about the troublesome keyword:

this is frustrating. Every time you think you have it, another weird case shows up - it should be simple, so why does it never seem to work how you want it to?

Why "this" is confusing

In other programming languages, this always refers to the current instance of an object. It's a very consistent keyword which will only ever hold two values: the current object, or nothing.

In JavaScript, this refers to what is known as the execution context. In practical contexts, this is deceptively similar to other languages version of this, but contains a fundament difference: the execution context is different based on how a function is called_._

This means that JavaScript's version of this may have different values depending on how you called the function.

class Foo {
    text = "string";

    trigger() {
        // Because of how the function is being called, `this` can have
        // several different values
        this.text = this.text + "a";
    }

    brokenTrigger() {
        // `this` will refer to the current object, so it will act as we expect
        this.trigger();

        // setTimeout resets `this` to the global context object - in web
        // browsers, it is the Window object
        setTimeout(this.trigger, 500);

        // When we refer to the function directly (without the object)
        // `this` refers to the global context object (window)
        const unboundFunction = this.trigger;
        unboundFunction();

        // Event listeners replace "this" with the target element 
        // `this` will refer to the clicked ".triggerButton"
        let button = document.querySelector(".triggerButton");
        button.addEventListener('click', this.trigger);
    }
}

Enter fullscreen mode Exit fullscreen mode

How to use this safely

When you see all the ways that this can go wrong, it seems like the easiest option is to throw your hands in the air, become a hermit and start a small potato farm.

In practice, this tends to be far less problematic than these examples make it appear. Most of the weird behaviours of this are easy to avoid by restricting your use of this to object functions, where it is the most consistent

As I said in the intro, using this with an object is almost always going to refer the object instance, but you do have to watch for two major exceptions:

  1. setTimeout
  2. addEventListener

In these cases, we have several techniques at our disposal to control the value of this, and to make sure that it works how we want.

Technique 1: Use Fat Arrow Functions

Fat Arrow Functions, aside from being a quick way of declaring functions, differ slightly from other function declarations in that they won't allow anything to overwrite this. Instead, it keeps the value from where the function is declared (its lexical scope).

What this means is that we can use them as wrappers, or directly as event listener function calls to preserve our this reference.

class Foo {
    listen() {
        // `this` still refers to Foo
        document.querySelector('.class').addEventListener('click', (e) => {
            this.handler(e); 
            // or
            this.val = 1;
        });
    }

    handler(e) {
        this.val = 1;
    }
}

Enter fullscreen mode Exit fullscreen mode

Technique 2: Assign this to a variable

Before ES6, a popular pattern was to copy the value of this when we knew it referred to our object and used the new variable instead.

var foo = {
    listen: function() {
        // These are both common names for our new `this`
        var that = this;
        var self = this;

        document.querySelector('.class').addEventListener('click', function() {
            self.val = 1;
        });
    }
}

Enter fullscreen mode Exit fullscreen mode

Technique 3: Explicitly set this with Function.bind

Functions come with several tools to set the value of this explicitly so you can guarantee the value of this.

  • Function.bind
  • Function.apply
  • Function.call

In practice, Function.bind is the most useful of the three, since it doesn't immediately call the function, instead returning a new version with a pre-set this, and any parameters you pass - you can use this new function directly in setTimeout or addEventListener function and keep your value of this.

class Foo {
    listen() {
        // The first paramter of `bind` is the new `this` value
        document.querySelector('.class').addEventListener('click', this.handleEvent.bind(this));
    }

    handleEvent() {
        this.val = 1;
    }
}

Enter fullscreen mode Exit fullscreen mode

Bonus Technique: Use Strict Mode

JavaScript's Strict Mode slightly changes the behaviour of this. Instead of implicitly setting this to the global context outsides of objects, it causes it to be undefined instead.

In practical terms, this is a fairly minor change, but it prevents several incorrect usages of this, and cause a would-be-hidden bug to throw an error instead:

'use strict';
let obj = {
    update(val) {
        // Normally this will create an `x` property on the global object and
        // continue running, but in strict mode this will throw an error
        this.x = val;
    }
}

// Breaking the reference to `obj` causes `this` to be undefined instead
// of referring to the global object
let func = obj.update;
func();

Enter fullscreen mode Exit fullscreen mode

Don't overcomplicate it

If you regularly read my blog, you'll know this is basically my slogan.

There's no denying that this is strange, but that doesn't mean that you need to worry about all the edge cases that it presents - More often than not, they don't come up.

I've been a Web Developer for coming on eight years now, and I learned some new edge cases about this when preparing this post that I have never encountered before.

If you're interested in learning more about JavaScript, you can check out this guide to closures, or read this article about what you need to know to start learning a front-end framework.


Having a hard time learning JavaScript? Sign up to my newsletter for articles like this straight to your inbox.

Top comments (0)