The This Threat

Ostensibly, JavaScript’s this keyword is “essential” to writing object-oriented code in the language. It’s supposed to reference “the current object,” like in its syntactic predecessor, Java. In any codebase using prototype or class, this appears everywhere.

It’s time to throw this into the garbage. The meaning of this too often differs from “the current object,” priming any code using this to fail unexpectedly. There is no strategy which adequately resolves the ambiguity of this. It should be completely eliminated from our vocabularies.

The Problem

Take this classic example:

function Employee (tasks) {
  this.tasks = tasks
  this.accomplishments = 0
}
Employee.prototype.work = function () {
  this.tasks.forEach(function () {
    this.accomplishments++
  })
}
var employee = new Employee(['this', 'that', 'the other thing'])
employee.work()
console.log(employee.accomplishments)  // 0
console.log(window.accomplishments)    // NaN

Here, employee.work() ought to set employee.accomplishments to 3. However, the code that counts the employee’s accomplishments has a bug…

In Employee.prototype.work, the this in the forEach callback won’t reference the Employee instance… instead, it will reference the global object! employee.accomplishments is 0, since it was never incremented. window.accomplishments is NaN, because this was window (the global object), and the property accomplishments of window was undefined, and undefined++ results in NaN.

The same issue occurs when passing references to other methods to forEach:

Employee.prototype.finishWork = function () {
  this.accomplishments++
}
Employee.prototype.work = function () {
  this.tasks.forEach(this.finishWork)
}

Witnessing examples like these, we can point and laugh as in wat. Experiencing bugs like these during our 9-to-5’s, we can pull our hairs out. Once we’ve had our fill of comedy and tragedy, we can obtain the knowledge necessary to make wise decisions and not repeat past mistakes.

There Is No Keeping this Without Pain And Suffering

Adult JavaScript programmers grip onto this like it’s their favorite teddy bear — except this teddy has a malfunctioning voice box that sometimes electrocutes a programmer when he squeezes his imaginary friend too tightly.

Do we cling onto this language feature? Or do we grow up, and realize we’d be happier without it?

Let’s examine what happens when programmers do the “clinging” thing.

Assigning this To A Variable

this can be assigned to a variable to maintain its binding lexically:

Employee.prototype.work = function () {
  var employee = this
  this.tasks.forEach(function () {
    employee.accomplishments++
  })
}

But what should the variable name be? employee, or that, or self, or…? Should all instances of this be replaced with the variable, or only the ones that “need it?” Isn’t it annoying to do var employee = this in a lot of places — perhaps every single damn method in the codebase? There aren’t any tools that will help enforce these coding styles, either.

Binding this With bind()

Using bind(), a copy of a function can be created where this is bound to a certain value.

This solution sucks. You have to remember to call bind() on every single damn function and method that needs it.

Things start out like this:

Employee.prototype.work = function () {
  this.tasks.forEach(function () {
    this.accomplishments++
  }.bind(this))
}

But end up looking like this:

Employee.prototype.work = function () {
  this.tasks.forEach(function () {
    this.somethingElse(function () {
      this.ohAndThisThing(function () {
        this.weCanDoThisAllDay(function () {
          …
        }.bind(this))  // don’t miss
      }.bind(this))    // a single one
    }.bind(this))      // or else
  }.bind(this))        // have fun debugging
}
Employee.prototype.weCanDoThisAllDay = function () {
  this.tryMe(function () {
    this.youAskedForIt(function () {
      …
    }.bind(this))
  }.bind(this))
}

Or they start like this:

function Employee () {
  …
  this.finishWork = this.finishWork.bind(this)
}

But become this:

function Employee () {
  …
  this.finishWork = this.finishWork.bind(this)
  this.somethingElse = this.somethingElse.bind(this)
  this.ohAndThisThing = this.ohAndThisThing.bind(this)
  this.weCanDoThisAllDay = this.weCanDoThisAllDay.bind(this)
  this.tryMe = this.tryMe.bind(this)
  this.youAskedForIt = this.youAskedForIt.bind(this)
}

Also, creating copies of functions can make event listeners impossible to detach, as the identities of the function objects will be different:

function Employee () {
  …
  button.addEventListener('click', this.work.bind(this))
}
Employee.prototype.work = function () {
  …
  button.removeEventListener('click', this.work)             // doesn’t work
  button.removeEventListener('click', this.work.bind(this))  // also doesn’t work
}

Using Special Parameters That Bind this

Employee.prototype.work = function () {
  this.tasks.forEach(function () {
    this.accomplishments++
  }, this)
}
Employee.prototype.work = function () {
  this.tasks.forEach(this.finishWork, this)
}

Some methods in the standard library have optional parameters which bind this specially. Since they’re there, people use them; but they’re no general solution, and you have to remember to use them every single damn time, just like with bind().

Banking On Strict Mode To Catch Undefined this

If you add a 'use strict' pragma to the beginning of a program or function, then in cases where this would be window, instead it will be undefined. Therefore, this.accomplishments++ would fail with an error.

'use strict'
…
Employee.prototype.work = function () {
  this.tasks.forEach(function () {
    this.accomplishments++  // TypeError: Cannot read property
                            // 'accomplishments' of undefined
  })
}

However, this hardly improves the situation, only making errors a little easier to notice, but not doing anything to prevent them.

Relying On Arrow Functions To Bind this Correctly

Arrow functions bind this lexically, which in the abstract would be an excellent solution:

Employee.prototype.work = function () {
  this.tasks.forEach(() => {
    this.accomplishments++
  })
}

Unfortunately, many API’s in JavaScript already bind this to a value which they consider “the current object,” which might make lexical this bindings the opposite of what’s expected.

For instance, jQuery binds this specially:

Employee.prototype.work = function () {
  $('#checklist li input[type="checkbox"]').each(function () {
    $(this).prop('checked', true)  // “this” is an HTML element
    this.accomplishments++         // “this” is not an Employee
  })
}

Arrow functions reverse the usual binding issue:

Employee.prototype.work = function () {
  $('#checklist li input[type="checkbox"]').each(() => {
    $(this).prop('checked', true)  // “this” is not an HTML element
    this.accomplishments++         // “this” is an Employee
  })
}

There are lots of other libraries that do this, including the DOM itself.

Calling Methods With A Caller

The issue of passing references to methods and losing their intended this binding can sometimes be resolved by calling the method from within a simple callback:

employee.tasks.forEach(employee.finishWork)                        // likely fails
employee.tasks.forEach((...args) => employee.finishWork(...args))  // succeeds

However, in functional code, patterns like the above “likely failing” one will work, since objects aren’t involved:

tasks.forEach(finishWork)

It sure would be nice if we could pass around references to methods with confidence.

Don’t Cling To this

The situations I’ve presented should make it obvious that introducing this into a JavaScript codebase can have a variety of negative consequences. These consequences can range from outbreaks of repetitive workarounds in localities contacting this, to subtle issues catching a programmer by surprise after long lying dormant, with a nasty debugging experience leaving him loathing past architectural choices.

You might still be forced to use this, if a library author binds this specially, or if his library must be extended with class, and you want to use that library regardless. In those cases, you’ll need to employ some combination of the aforementioned “solutions.” At least after having read this post, you may now be keener to issues involving this, and be able to avoid them most of the time.

Don’t use this when you have the choice! Let’s stand strong against the this threat.

To Be Continued

Ideally, we will change the way we write JavaScript, recognizing that we do not need this to write object-oriented code in the first place. I explain why and propose a simpler and more reliable pattern for OOP in another blog post. Click here to read it.