This is not always as it seems

26 June 2011

Talking about this can be very difficult given that this is this. With that in mind I’m going to try to avoid using the English ‘this’ as much as possible. When referring to the scope, I’ll use this.

What is this? (ok last one i promise)

The scope relates to the owner of the method which is accessible through the reserved word this. By default, this is the window object.

alert(this === window); //true
(function () {
  alert(this === window); //true
}())
function fn() {
  alert(this === window);
}
fn(); //true

Defining a scope

All functions in javascript can be referenced in five ways

  • fn() directly reference fn
  • var ourself = new fn() creates a copy of fn as ourself
  • fn.call(ourself, argument, argument, argument) using the call method to define a scope and set of arguments as a comma separated list
  • fn.apply(ourself, arguments) using the apply method to define a scope and set of arguments as an ‘array’
  • setTimeout(fn, 1000) passing it as an argument to be called later

Modifying the scope of a closure

Here is a typical anonymous closure.

(function () {
  alert(this); //window
}());

The above can also be expressed as:

alert(this); //window
(function () {
  alert(this); //window
}.call(this));

The first anonymous closure uses the scoping of it’s parent, which is window. The second anonymous closure is fired using the call method with this as the scope, which we know to be window. Which means we can define the scope of an anonymous closure to be whatever we like.

var o = 'ourself';
alert(this); //ourself
(function () {
  alert(this); //ourself
}.call(o));

Delayed execution and intervals

Consider the following script which we would like to alert ‘ourself’ after a delay.

(function () {
  setTimeout(function() {
    alert(this); //window
  }, 1000);
}.call('ourself'));

Unfortunately, the above script would alert window and not ourself. As is turns out, the setTimeout and setInterval methods are actually window.setTimeout and window.setInterval methods so the owner/scope is window. To use the scope we want, we need to store the scope in a variable that the setTimeout has access to.

(function () {
  var self = this;
  setTimeout(function() {
    alert(self); //ourself
  }, 1000);
}.call('ourself'));

Events

Scope changes for events too, which is sometimes useful, but mostly annoying. Take this example (I’m going to ignore the required if statement (for the different listener methods, thanks again IE).

(function () {
    var btn = document.getElementById("btn");
    function clicked(e) {
        alert(this);
    }
    btn.addEventListener('click', clicked, false);
    btn.attachEvent('onclick', clicked);
}.call('ourself'));

The alert(this) will return btn, or window in IE, when what we really want is the element and event object as an argument and ourself as the scope.

(function () {
    var self = this,
        btn = document.getElementById("btn");
    function clicked(e, el) {
        alert(this); //ourself
        alert(el); //btn
    }
    btn.addEventListener('click', function(e) {
        clicked.apply(self, [e, this]);
    }, false);
    btn.attachEvent('onclick', function(e) {
        clicked.apply(self, [e, btn]); //ie will always 
    });
}.call('ourself'));

Basic rules to code (this) by

This is the owner of the method, which is defined as the caller of the function / method.

fn(); // this = window
ourself.init(); // this = ourself
ourself.aMethod.anotherMethod(); // this = ourself.aMethod

This can be stored as a variable (though i prefer to think of it as an alias), and any changes to the alias or this are reflected by both;

(function () {
    var self = this;
    self.bye = 'bye';
    this.hello = 'hello';
    alert(this === self); //true
}.call({}));

This can be set by using the call and apply methods.

that.aMethod.call(ourself); // this = ourself

Matt Sain

Ramblings of a developer, designer and child subscribe

This blog is a public GitHub repository. If you find an error I will not be surprised... but if you fork and edit the blog and send me a pull request you'd be pretty awesome in my book.

Featured Repos