diessi.caBlog
March 15, 2017

var that = this? Nah.

Tried JavaScript for some minutes? You probably already wrote the following.

var that = this;

(Or one of its variants, such as var self = this.)

That’s a pretty common pattern when you found yourself in a situation where:

  1. you have a function nested inside of an object’s method, and
  2. you want to access the object’s properties, but
  3. JavaScript’s this is bound in an unexpected way.

Workarounding unexpected callback scope is one of the main situations where developers write var self = this.

Let’s assume a piece of code with a callback function.

function Requester(data, req) {
  this.data = data;

  req.done(function () {
    console.log(this.data); // undefined
  });
}

You’d expect this.data to be the data argument. Yet, JavaScript tells you that it is actually undefined.

So, because Stack Overflow says so, you do the following:

function Requester(data, req) {
  this.data = data;
  var self = this; // NOOOOOoOooo

  req.done(function () {
    console.log(self.data);
  });
}

And never stops doing it in your life.

Still do it? I’m flattered to tell you that you don’t need that extra variable anymore.

Arrow functions

Go with arrow functions instead. Besides having a shorter syntax, arrow functions bind to the parent scope by default.

The scope that was firstly intuitive to you will work as expected now.

function Requester(data, req) {
  this.data = data;

  req.done(() => {
    console.log(this.data); // intuitive as hell!
  });
}

You can count on them most of the time, but they also have unexpected behaviour in more advanced cases. After all, you're dealing with the `this` keyword. So don't just go around refactoring all anonymous functions into arrow ones and expecting everything to work.

You may also .bind()!

If for some reason (such as no support for IE) you can’t use them, check JavaScript’s bind. It’s not as awesome as arrow functions, but does the job.

function Requester(data, req) {
  this.data = data;

  req.done(
    function () {
      console.log(this.data);
    }.bind(this)
  );
}