Definitive way to use "this" in JavaScript

Go To StackoverFlow.com

1

I am beginning to define classes in JavaScript, and I have a lot of troubles with the keyword this.

Here is an example of what I want to do:

function MyMap() {

    this.map = new google.maps.Map(....);

    google.maps.event.addListener(this.map, 'idle', function() {
        this.mapIdle();    // PROBLEM: "this" undefined
    });

    this.mapIdle = function() {
        google.maps.event.addListener(marker, 'click', function() {
            $("button").click(function() {
                $.ajax({
                    success: function() {
                        this.map.clearInfoWindows(); // PROBLEM: "this" undefined
                    }
                });
            });
        });
    }
}

As you can see in the comments, this won't work here, because it is used inside a closure.

I have started using workarounds like:

var that = this;
google.maps.event.addListener(this.map, 'idle', function() {
    that.mapIdle();
});

Or even where you have to define a callback function around your callback function (seriously!!).

This is extremly ugly and doesn't work everywhere. When I get a lot of nested lambda functions (as in the example I gave), I have no idea how to use a class attribute.

What is the best and most correct way to do that?

2012-04-03 22:01
by Matthieu Napoli
As a side note, you may find this helpful: Fully Understanding the <code>this</code> Keyword - Colin Brock 2012-04-03 22:04
Also as a side note, you'll very rarely find "the definitive way" to do anything in Javascript - it's just not that kind of language - nrabinowitz 2012-04-03 22:06


3

The easiest way is to define a self variable (or that, if you don't mind a non-semantic variable name) as you already mentioned:

function MyMap() {
   var self = this;

   // in nested functions use self for the current instance of MyMap
}

Noting that you have to do it again for methods you add to the prototype (if they use nested functions):

MyMap.prototype.myMethod = function() {
   var self = this;
   // use self in nested functions
};

You should also read up on the .bind() method, noting that it doesn't work for IE <= 8.

The question you linked to about defining a callback around your callback is to solve a different problem, i.e., setting up an appropriate closure structure to allow functions that are nested inside a loop to have access to the appropriate loop counter value(s). That has nothing to do with the this issue. (And can easily be combined with the self technique when needed.)

2012-04-03 22:22
by nnnnnn
I do a thing like this. I sometimes call it public to contrast from a private scope/sub-object - Umbrella 2012-04-03 23:00
This is working very well, thanks a lot. Javascript can be a pain sometimes.. - Matthieu Napoli 2012-04-04 17:45


1

If you're using jQuery, $.proxy is handy for this:

http://api.jquery.com/jQuery.proxy/

2012-04-03 22:08
by Pete


1

You can just assign the object and the function to local varibles, then they are included in the closures for the callback functions:

function MyMap() {

  var map = new google.maps.Map(....);

  var mapIdle = function() {
    google.maps.event.addListener(marker, 'click', function() {
      $("button").click(function() {
        $.ajax({
          success: function() {
            map.clearInfoWindows();
          }
        });
      });
    });
  };

  this.map = map;
  this.mapIdle = mapIdle; // Is this needed?

  google.maps.event.addListener(this.map, 'idle', function() {
    mapIdle();
  });

}
2012-04-03 22:12
by Guffa


1

In JavaScript, this is reassigned with every single function call.

It's just something you have to be aware of in JavaScript. It can be confusing at first, but once you know a few simple rules, it's actually pretty straightforward.

If it's a method call like myObj.doSomething(), then this will be automatically set to myObj inside of doSomething().

If you want to explicitly control the value of this when making a function call, you can use doSomething.apply() or doSomething.call() to control what this is set to inside the function. That's what event handler callbacks do. They explicitly set this to point to the object that created the event (something which is very useful). You can read more about .apply() and .call() on MDN.

If you just call a regular function, then this will be set to the global object which in a browser is the window object.

All callback functions will have their value of this messed with because every function call changes this. Since your event handler is a callback and the success handler is a callback in the Ajax function, you should expect that the value of this will not be preserved from the surrounding code. There are work-arounds using proxy or bind functions, but usually it's just as easy to capture the previous value of this in a closure and just access it from there with something like var self = this;.

In your circumstance, when you want access to a this pointer from outside the event handler, the right thing is to just save it to a local variable that you will have access to in the event handler or even in the Ajax call that is in the event handler. There is no cleaner way to do it. This way you have access to both the this pointer from the event or the Ajax call and the this pointer from your calling object like this:

    var self = this;
    self.mapIdle = function() {
        google.maps.event.addListener(marker, 'click', function() {
            $("button").click(function() {
                $.ajax({
                    success: function() {
                        self.map.clearInfoWindows(); // PROBLEM: "this" undefined
                    }
                });
            });
        });
    }
}
2012-04-03 23:56
by jfriend00
Ads