487-js-this-gotchas

JavaScript ‘this’ Gotchas

By | | JavaScript

8

In my previous post, we looked at JavaScript’s this statement and how it can change depending on the context of the function call. Today, we’ll examine several situations where this could catch you out…

1. Forgetting ‘new’

Consider the following code:


window.WhoAmI = "I'm the window object";
function Test() {
  this.WhoAmI = "I'm the Test object";
}
var t = Test();
alert(window.WhoAmI); // I'm the Test object
alert(t.WhoAmI); // t is undefined

What we really meant is:


var t = new Test();

The omission of the new statement gave us undesirable results. Other languages would throw an error when faced with a direct call to a constructor but JavaScript simply treats it like any other function call. this is taken to be the global window object and no value is returned from Test() so t becomes undefined.

This situation can be fixed if you’re writing a JavaScript library for third-party developers. Refer to Fixing Object Instances in JavaScript.

2. Module madness

This one will give you a headache. Examine the following code which uses a module pattern:


window.WhoAmI = "I'm the window object";
var Module = function() {
  this.WhoAmI = "I'm the Module object";
  function Test() {
    this.WhoAmI = "I'm still the Module object";
  }
  return {
    WhoAmI: WhoAmI,
    Test: Test
  };
}();
alert(Module.WhoAmI); // I'm the Module object
alert(window.WhoAmI); // I'm the Module object
Module.Test();
alert(Module.WhoAmI); // I'm still the Module object

The code looks logical — so why is window.WhoAmI saying it’s the module object?

We need to remember that we have a self-executing function. It’s results are returned to the Module variable but, when it’s first run, Module doesn’t exist. this is therefore the global window object. In other words, this.WhoAmI = window.WhoAmI = "I'm the Module object".

The function returns a JavaScript object with a WhoAmI property with a value of 'WhoAmI'. But what does that refer to? In this case, the JavaScript interpreter works up its prototype chain until it magically finds window.WhoAmI ("I'm the Module object").

Finally, we run the Test() method. However, Module has now been created so, within the Test function, this refers to the Module object so it can correctly set the WhoAmI property.

In summary, avoid using this within a module to refer to the module itself! You should never need it.

3. Method misconceptions

Here’s another JavaScript pattern which will screw with your synapses:


var myObject = {};
myObject.method = function() {
  this.WhoAmI = "I'm myObject.method";
  function Test() {
    this.WhoAmI = "I'm myObject.method.Test()";
  }
  Test();
  return this.WhoAmI;
};
alert(myObject.method()); // I'm myObject.method

In this example, Test() is a private function executed within myObject.method(). At first glance, you would expect this within Test() to reference myObject. It doesn’t: it refers to the global window object since it’s just another function.

If you want to reference myObject within the private function, you’ll require a closure, for example:


var myObject = {};
myObject.method = function() {
  this.WhoAmI = "I'm myObject.method";
  var T = this;
  function Test() {
    T.WhoAmI = "I'm myObject.method.Test()";
  }
  Test();
  return this.WhoAmI;
};
alert(myObject.method()); // I'm myObject.method.Test()

4. Referencing methods

Here’s a little code which, fortunately, will work exactly as you expect:


var myObject = {};
myObject.WhoAmI = "I'm myObject";
myObject.method = function() {
  this.WhoAmI = "I'm myObject.method";
};
// examine properties
alert(myObject.WhoAmI); // I'm myObject
myObject.method();
alert(myObject.WhoAmI); // I'm myObject.method

Let’s make a minor change and assign myObject.method to another variable:


// examine properties
alert(myObject.WhoAmI); // I'm myObject
var test = myObject.method;
test();
alert(myObject.WhoAmI); // I'm myObject

Why hasn’t myObject.WhoAmI changed? In this case, the call to test() acts like a regular function call so this refers to the window object rather than myObject.

If you think that’s nasty, wait until we take a look at JavaScript event handlers in my next post!

note: Want more?

If you want to read more from Craig, subscribe to our weekly tech geek newsletter, Tech Times.

Get Started with
Ruby on Rails

Github, Twitter and Hulu. All huge. All successful. All Rails.

Learn the web development framework of the moment with our newest book and course.

Learn Rails

Craig Buckler

Craig is a Director of OptimalWorks, a UK consultancy dedicated to building award-winning websites implementing standards, accessibility, SEO, and best-practice techniques.

More Posts - Website

{ 8 comments }

goldfidget April 6, 2011 at 6:51 pm

Test is private precisely because it’s not an attribute of myObject.method, therefore not accessible from outside. It feels strange because it seems as though when you declare a variable in an object, that variable should be an attribute of the object, but that isn’t the case here.

I think there are two separate processes going on:

1. the whole object declaration thing which can be achieved using object literals or the dot syntax.
2. The code in the function, which is itself an implicitly defined attribute of the function object.

I think my brain is finally starting to contain this, unless I’m way off the beam :)

goldfidget April 6, 2011 at 6:27 pm

So let me get this straight. For point 3, the Test function comes into existence when myObject.method is called and goes away when myObject.method finishes. It’s a local variable, called test, and not part of the myObject object.

My conceptualisation of this is that:

1. Functions are objects.
2. Objects are associative arrays – name value pairs.
3. The code of a function is just an attribute of the function object.
4. Therefore, declaring a variable inside the function object is not enough to set it as an attribute of that function.
5. “this” is defined relative to the object you are currently operating inside.
6. The Test function is an object, but not an attribute of myObject, so “this” = window.

Is this an accurate take on things? Also, is Test ever an attribute of the window object, or does it just exist in some list of temporary variables?

JF Goude March 24, 2011 at 5:12 pm

Point 1 affecting variable t the returning result of function Test() (var t = Test();) affects t with ‘undefined’ as Test() function doesn’t return anything.
writing this :
var t=Test;
gives proper result, variable t is a pointer to function Test and t.WhoAmi is OK

Loic March 18, 2011 at 9:10 pm

For the point 3, instead of using closure you could also do that:

var myObject = {};
myObject.method = function() {
this.WhoAmI = “I’m myObject.method”;
function Test() {
this.WhoAmI = “I’m myObject.method.Test()”;
}
Test.apply(this);
return this.WhoAmI;
};
alert(myObject.method()); // I’m myObject.method.Test()

Stefan March 16, 2011 at 6:36 pm

If you want to define methods iso just function you should always assign them to an object for example:

var obj = {};
var obj.method = function () {
this.attr = ““;
this.Test = function () { this.attr = “
“; }
this.Test();
return this.attr;
}
alert(obj.method()); //

Andrew March 16, 2011 at 9:04 am

Thanks for this. I’m always happy to see explanations in what seems like an esoteric language at times.

Brandtley McMinn March 16, 2011 at 1:23 am

Hey Craig, The subscription call to action section at the end of your post is broken. I think ya’lls wordpress is sanitizing the HTML in the post.

Great post though. I’ve had a number of issues lately with this.method() not returning expected values, didn’t realize it was referencing the global window object. I finally got my this.method(args) functioning properly :)

Cheers

Craig Buckler March 16, 2011 at 1:35 am

Cheers Brandtley – the link’s fixed now.

Comments on this entry are closed.