Tuesday, February 03, 2015

self = this

I teach JavaScript, and one of the things I've found most confuses people is the self = this pattern (some people even go to great lengths to avoid learning about it). I haven't been able to find a good post on it, so here's my attempt. If you're not interested in JavaScript, feel free to skip this and read some of my other fine posts.

Okay, I'll start with a concise definition and then explain in depth.

Use the self = this pattern whenever you have a function nested inside of an object's method, and you want to access the object's properties.

Assign self in the object's method and use it inside the nested function. Here's how it looks:

var myObject = {
  aMethod: function() {
    var self = this;

    this.aProperty = 'foo';

    setTimeout(function() {
      console.log(self.aProperty); // outputs 'foo'
    }, 1);
  }
};

So, let's explain further.

Objects with Functions

While you can certainly write plenty of JavaScript without ever attaching a function to an object, I find that organizing my code into objects makes things a lot cleaner. To avoid junking up the global namespace, I usually define a single global object (named "App" or some such) that all of my variables and functions are attached to. To further organize, I'll group related functions and variables into sub-objects attached to the main object.

Most objects will have a mix of variables (called properties when they are part of an object) and functions (called methods when they are part of an object). Here's a simple object with one of each:

var myObject = {
  aProperty: 'foo', 
  aMethod: function() {
  }
};

Inside an object's methods, there's a special variable, named this, that is a reference to that object. If the method wants to access any of the object's properties or other methods, it does so through that variable, like so:

var myObject = {
  aProperty: 'foo', 
  aMethod: function() {
    console.log(this.aProperty); // outputs 'foo'
  }
};

This special variable is managed by JavaScript; you don't have to assign it, and in fact you can't. It's assigned automatically for every function. If it's an object method, this is a reference to the object. For any other object, this is a reference to the global variable namespace.

Nested Functions

In JavaScript, you can nest functions inside of other functions, including object methods. This is a common practice when working with user events, network requests, timers, etc.

var myObject = {
  aMethod: function() {
    setTimeout(function() {
    }, 1);
  }
};

One handy feature of nested functions is that the inner function has access to all of the variables defined in the outer function. This is called closure.

var myObject = {
  aMethod: function() {
    var a = 'foo';

    setTimeout(function() {
      console.log(a); // outputs 'foo'
    }, 1);
  }
};

The one exception is the special this variable; since the nested function is just a regular function, not an object method, its this is a reference to the global namespace.

var aProperty = 'global';

var myObject = {
  aMethod: function() {
    this.aProperty = 'local';
    
    setTimeout(function() {
      console.log(this.aProperty); // outputs 'global'
    }, 1);
  }
};

Putting It All Together

So, we need a way to get a reference to the object to be available inside the nested function. Closure to the rescue! We assign another (non-special) variable, e.g. self, with the object from this. Since this variable isn't overwritten for each function (unlike this), we can use it inside the inner function.

var myObject = {
  aMethod: function() {
    var self = this;

    this.aProperty = 'foo';

    setTimeout(function() {
      console.log(self.aProperty); // outputs 'foo'
    }, 1);
  }
};

Notes

  • There's nothing magic about the word self; you can use whatever variable name you want. However, self is the most commonly used variable name for this pattern, so it's a good choice for clarity's sake.
  • If a method needs a var self = this; line, I always put it at the very top of the method. Putting it at the top isn't absolutely necessary, but I think it's a good convention; that way you don't have to go hunting to see if it's defined.
  • this is only set up properly if you call the method via the object, like myObject.aMethod(). If you instead call the method indirectly, such as when you pass it to another function (e.g. setTimeout(myObject.aMethod, 1)), this won't be set up. To deal with this, we use closures again, like so: setTimeout(function() { myObject.aMethod(); }, 1).
  • It's actually possible in certain cases for this to not be the object or the global namespace. By using JavaScript's apply or call features, you can set this to whatever you want when you're calling a function. jQuery uses this in its event handlers, for instance.