views:

94

answers:

2

I make some Ajax calls from inside a javascript object.:

myObject.prototye = {
  ajax: function() {
    this.foo = 1;

    var req = new XMLHttpRequest();
    req.open('GET', url, true);
    req.onreadystatechange = function (aEvt) {  
      if (req.readyState == 4) {  
        if(req.status == 200)  {
          alert(this.foo); // reference to this is lost
        }
      }
  }
};

Inside the onreadystatechange function, this does not refer to the main object anymore, so I don't have access to this.foo. Ho can I keep the reference to the main object inside XMLHttpRequest events?

+5  A: 

The most simple approach is usually to store the value of this on a local variable:

myObject.prototype = {
  ajax: function (url) { // (url argument missing ?)
    var instance = this; // <-- store reference to the `this` value
    this.foo = 1;

    var req = new XMLHttpRequest();
    req.open('GET', url, true);
    req.onreadystatechange = function (aEvt) {  
      if (req.readyState == 4) {  
        if (req.status == 200)  {
          alert(instance.foo); // <-- use the reference
        }
      }
    };
  }
};

I suspect also that your myObject identifier is really a constructor function (you are assigning a prototype property).

If that's the case don't forget to include the right constructor property (since you are replacing the entire prototype), which is simply a reference back to the constructor.

Maybe off-topic to this issue but recommended to read:

CMS
+2  A: 

An other simple solution is to bind your onreadystatechange function to this. bind -ing the function does essentially the same thing as in CMS's answer (that is, adding the value to the closure), but bind does it in a transparent way: you keep using this instead of setting a instance variable.

Here is a Function#bind implementation if your code base doesn't include one :

Function.prototype.bind = function(obj) {
    var __method = this;
    var args = []; for(var i=1; i<arguments.length; i++) args.push(arguments[i]);
    return function() {
        var args2 = []; for(var i=0; i<arguments.length; i++) args2.push(arguments[i]);
        return __method.apply(obj, args.concat(args2));
    };
}

And here is how you can use it in your code :

myObject.prototype = {
  ajax: function() {
    this.foo = 1;

    var req = new XMLHttpRequest();
    req.open('GET', url, true);
    req.onreadystatechange = function (aEvt) {  
      if (req.readyState == 4) {  
        if(req.status == 200)  {
          alert(this.foo); // reference to this is *kept*
        }
      }
    }.bind(this) // <- only change
  }
};
Alsciende
I would strongly recommend to check if the `Function.prototype.bind` method exists before creating it, because it is part of the ECMAScript 5th Edition Standard and it is being implemented by all major browser vendors, soon a native implementation will be available and you don't want to override it with your own: [WebKit](https://bugs.webkit.org/show_bug.cgi?id=26382), [Mozilla](https://bugzilla.mozilla.org/show_bug.cgi?id=429507), [Google V8](http://code.google.com/p/v8/issues/detail?id=384).
CMS