Understanding JavaScript's this keyword

From Trephine

Revision as of 17:51, 26 March 2009 by Jimbojw (Talk | contribs)
(diff) ←Older revision | Current revision (diff) | Newer revision→ (diff)
Jump to: navigation, search
« Bespin trepanation JavaScript loop closures »

[subscribe] Recent blog entries

Live Demos

Understanding JavaScript's this keyword

A few days ago I wrote about Aspect Oriented JavaScript, explaining how to replace a given function or method with a wrapper. To fully understand that concept, a proper understanding of JavaScript's treatment of this is helpful. This article aims to provide a simple overview of how this is determined in various JavaScript contexts.

Let's start with an example. Consider the Bob:

var Bob = {
  name: "Bob",
  greet: function() {
    alert( "Hi, my name is " + this.name );
  }
};

Here, we've created an Object instance which has two members:

  • name, a simple string, and
  • greet, a method containing a reference to this.name.

When we execute Bob.greet(), the greet function is executed with Bob as the this object.

Now let's consider what happens when we co-opt Bob's greet on a new object:

var Alice = {
  name: "Alice",
  greet: Bob.greet
};

Which of the following should Alice.greet() to produce?

  1. "Hi, my name is Bob", or
  2. "Hi, my name is Alice"

If you answered "#2", you are correct! The this.name reference in Bob's greet function is not resolved until the function is actually executed. When Alice.greet() is called, the greet function's this is set to Alice.

But what if the function is called by itself, not as a method on an object? Consider this:

var unboundGreet = Bob.greet;
unboundGreet();

What do we expect unboundGreet() to produce? The answer is "Hi, my name is ". The reason is that this is set to the window global when an unbound function is called, and since window.name is undefined, it becomes the empty string during concatenation.

Here's an example where implicit unbinding can become a debugging headache:

setTimeout( Bob.greet, 1000 ); // Call Bob.greet 1 second from now

The developer expectation might be that Bob's greet would be called in 1 second, bound to Bob, thus producing "Hi, my name is Bob" - but that is not what happens. Instead, the greet method is called unbound, producing the same result as unboundGreet().

It might be a little clearer to see why this is by looking at an equivalent snippet:

var unboundGreet = Bob.greet;
setTimeout( unboundGreet, 1000 );

In this case, it should be more obvious that the greet function will be not bound at execution time (1 second hence).

The same thing happens when trying to set an Object's method as an event handler. Consider this HTML:

<a id="clickme" href="">Greetings!</a>

And this JavaScript:

document.getElementById("clickme").onclick = Bob.greet;

The expectation may be that this would cause the clickme anchor to produce "Hi, my name is Bob" when clicked, but the result is (once again) the same as calling unboundGreet().

The solution to the implicit unbinding problem is to create a function which wraps the bound method. Many JavaScript libraries contain helpers to do this, often times by adding a bind method to the Function.prototype.

Without getting into that though, here's a simple example of how to create such a wrapper:

var bobsGreet = function() {
  Bob.greet();
};
setTimeout( bobsGreet, 1000 );

Or, more simply:

setTimeout( function(){
  Bob.greet();
}, 1000 );

That's it for this overview. If this helped you understand the concept better, or if you have any questions, please drop me a comment!

Update: If you liked this, check out JavaScript loop closures for more scoping goodness!

--Jim R. Wilson (jimbojw) 14:39, 18 March 2009 (UTC)