Caffeinated Simpleton

An Introduction to JavaScript’s “this”

JavaScript is an amazing little language, but it’s got some quirks that turn a lot of people off. One of those quirks is this, and how it’s not necessarily what you expect it to be. this isn’t that complicated, but there are very few explanations of how it works on the internet. I find myself constantly re-explaining the concept to those who are new to JavaScript development. This article is an attempt to explain how this works and how to use it properly.

What is this?!

this is the current object. People that are used to object oriented languages use objects constantly, and this is how you access your object in JavaScript. This differs from Java and C++. This is best demonstrated by example, so let’s take a look.

class HotDog {
public:
     string getCondiments();
private:
     string condiments;
}
 
string HotDog::getCondiments() {
    return condiments; // The compiler knows to find "condiments" in the current HotDog instance
}

Ruby and Python don’t work this way. Instead, you must say “look for this variable in the current instance”

class HotDog:
    condiments
    def getCondiments(self):
         return self.condiments # "self" is a reference to the current instance of HotDog.
  class HotDog
         @condiments
         def getCondiments
              return @condiments # @condiments is an instance variable of an instance of HotDog
         end
    end

JavaScript has more in common with Ruby and Python than with Java and C++. The same thing in JavaScript is:

function HotDog() {
    this.condiments = "mustard, ketchup";
    this.getCondiments = function() {
         return this.condiments; //this is expected to be a reference to the current instance
    }
}

That’s what this is expected to be, anyway. It’s expected to be a reference to the current instance of whatever object it’s defined within.

this Gets Tricky

However, let’s say we wanted to find out what condiments were on our hotdog in 30 seconds. Assuming the HotDog class from above, that code might look like this:

var myHotDog = new HotDog();
// Call the getCondiments function in 3 seconds
setTimeout(myHotDog.getCondiments, 3000);

Many JavaScript beginners are surprised to learn that this code won’t work. It’ll give an error saying that this doesn’t have a member called condiments, even though it clearly does. What happened?!

As it turns out, this in the getCondiments function is set to the global object, window. This is because there is no binding of functions to instances in JavaScript. Whenever the instance isn’t explicitly stated, then this becomes window (at least in the browser). Writing this as myHotDog.getCondiments() indicates that you want this to be myHotDog, so it works correctly. The setTimeout function, however, just has a reference to that function. When it calls it, it’s not aware of myHotDog, so JavaScript sets this to window

“Fixing” this

There are several ways of forcing this to be what you want, and many of them touch on some of the more interesting features available in JavaScript. The first is apply. apply is a member of every function. It says “call this function with this object as this and these arguments. An example might help.

// These two are equivalent
myHotDog.getCondiments();
myHotDog.getCondiments.apply(myHotDog);
 
// You could also do this
var yourHotDog = new HotDog();
myHotDog.getCondiments.apply(yourHotDog);
 
// The above line does the same thing as:
yourHotDog.getCondiments();
 
// The above two calls will return the condiments off of your hot dog, not mine.

That doesn’t solve our setTimeout problem, however. apply actually calls the function, and we just want a reference to the function that uses the correct this. Luckily this is easily done, but let’s talk about some background first.

Lexical Scoping

In many languages (all the good ones, if you ask me), lexical scoping is supported. Lexical scoping basically allows you to access local variables in a parent function. If a function is defined within another function, it can access its own local variables as well as those of the function it was defined within. Time for another example.

function drinkBeer() {
    var beer = "Big Daddy IPA";
    function pour() {
         var glass = "Pint Glass";
         return "Poured " + beer + " into a " + glass;
   }
   function drink() {
         beer = null;
         return "Beer is all gone";
   }
   pour();
   drink();
}
drinkBeer();

This works just fine. drinkBeer cannot access glass, but pour and drink can access beer.

Lexical scoping is extremely powerful. If this small example doesn’t explain it, I encourage you to look around other examples on the internet until it’s more clear. While writing JavaScript, look out for ways this can make your life easier. Once you’re looking, they’re all over the place.

Solving our setTimeout Problem

With lexical scoping, we can easily solve our setTimeout problem.

var myHotDog = new HotDog();
setTimeout(function() {
    myHotDog.getCondiments();
}, 3000);

See what we did there? We created a new function that we passed to setTimeout. Our new function can access myHotDog, so it just applies it to the getCondiments function. Pretty slick.

Defending against this abuse

As somebody writing the HotDog class, it might be upsetting to you that people constantly need to keep this in mind when accessing your class. It would be nicer if there was a way to make your class work all the time. Fortunately, there is!

function HotDog() {
    var my = this; // my references the current this, which is correct.
    my.condiments = "mustard, ketchup";
    my.getCondiments = function() {
         return my.condiments; //my is guaranteed to be a reference to the original "this"
    }
}

HotDog is a constructor. You instantiate a new instance of HotDog by writing “new HotDog()“. In constructors, this is always your instance. So we created a new variable, my, that references the HotDog instance. This allows you to always refer to the HotDog instance, no matter how the getCondiments function is called.

Bind

Another method of “fixing” this is adding a bind attribute to every function. This allows you to pass in an object that will be your this. Many JavaScript libraries, such as Prototype, do this.

var boundFunction = myHotDog.getCondiments.bind(myHotDog);

Now boundFunction will always call getCondiments with this set to myHotDog. If you’ve been paying attention, it should be fairly obvious how bind is written.

function bind(thisObject) {
    var my = this; // this is the function here
    return function() {
        my.apply(thisObject);
    }
}

Cobra

My own class library, cobra, solves the this problem in a different way. It passes a reference to the original this to every function, which allows you to use some fancy features like prototypal inheritance while still not worrying about binding and the like. If you’re interested, you can find the code on bitbucket.

Whew

That’s pretty much all you need to know about this.

  • this will be window whenever your function is indirectly called.
  • Use apply to force this to be what you want
  • You can use lexical scoping to make sure this behaves in a predictable manner

I hope that’s clear, comment if it’s not!

Tags: ,

This entry was posted on Saturday, September 26th, 2009 at 5:27 pm and is filed under Development. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

  • ls_n
    There are a few more intricacies to 'this'. I've attempted to address most of them here: http://lucassmith.name/pub/this.html
  • Good article. I have been very confused by "this".
  • hugobryn
    There is a bit more to the setTimeout() function to be totally clear:

    1) setTimeout(myObj.myFunc, 1000) - this set to global window object

    2) setTimeout(function() { myObj.myFunc() }, 1000) - this set to myObj as we want it.

    But you can also pass a string to setTimeout and basically setTimeout() becomes a sort of eval:

    3) setTimeout('myObj.myFunc()', 1000) - this set to myObj

    Number 2 is the best way to go though.
  • Marcos
    All that stuff looks like PHP. Disgusting language JavaScript, ughhh...
  • seanja
    Wait, php or javascript? At least javascript has some consistency to it, php... not so much
  • seanja
    As was pointed out to me on Stackoverflow.com, you can also solve it by using closures http://stackoverflow.com/questions/1466875/ies-...
  • i used methods introduced in http://odetocode.com/Blogs/scott/archive/2007/0... to solve 'this' problems
  • DougBTX
    In the "solving our setTimeout problem" code sample, why do you use apply? At this point you have a reference to the object, so you can call myHotDog.getCondiments() directly.
  • You're right that it's unnecessary. I was trying to combine the previous point, but it might just make things more confusing. I might change it.
  • Yeah, I think you're right DougBTX. I've changed it.
  • DossyDomo
    Oh wow, that actually makes sense to me dude, I like it!

    RT
    www.total-privacy.net.tc
  • Using:

    var my = this;

    Is such a beautiful fix, I'm amazed by its simplicity. Cheers!
  • Nik
    // Call the getCondiments function in 30 seconds
    setTimeout(30, myHotDog.getCondiments);


    Where do you get that strange setTimeout? To call myHotDog.getCondiments in 30 seconds, this is the way to go:

    setTimeout(myHotDog.getCondiments, 30 * 1000);
  • You're right about the order, it's been corrected. I don't really believe in putting millisecond timeouts in examples since it's needlessly detail oriented.
  • You still have to fix it in the first example (under "this Gets Tricky").
    Also, the following works just fine for me, in various browsers (IE7/8, FF)? No errors on the console and it happily alerts after 3 seconds.

    function HotDog() {
    this.condiments = "mustard, ketchup";
    this.getCondiments = function() {
    alert(1);
    return this.condiments;
    }
    }

    var myHotDog = new HotDog();

    setTimeout(myHotDog.getCondiments, 3000);
  • Updated the first example. Thanks!
  • budda
    It will work if you call it once in a function-block.
    The scope of the timeout-callback will be the same as where it was called.
    try this:
    for(var i=0;i<3;i++)
    {
    setTimeout(alert(i), 500); //Will write 3, 3 times because i is 3 when alert is called
    }

    vs

    for(var i=0;i<3;i++)
    {
    var a = i;
    setTimeout(a, 500); //Will write 0, 1, 2 because a is only within the scope of the current iteration
    }


    Google closures for more info
  • Variable scope in JavaScript is function. There is no block scope.
    Defining variable 'a' with a 'var' statement inside the loop is just misleading. JavaScript is not Java.
    So the two version do the same.

    Also you didn't test your code at all.. Else you would have seen that alert is not called by setTimeOut but instead inside the loop.
    You should have written this:
    setTimeout(function { alert(i) }, 500);
  • mspreij
    Ok, I was wrong :-) when doing alert(this.condiments) instead it says "undefined". Now I also see why setting var me = this; helps.
blog comments powered by Disqus