views:

25

answers:

1

this thing almost works:

function myClass(url) {

this.source = url;
this.rq = null;
this.someOtherProperty = "hello";

// open connection to the ajax server
this.start = function() {
    if (window.XMLHttpRequest) {
        this.rq = new XMLHttpRequest();
        if (this.rq.overrideMimeType)
        this.rq.overrideMimeType("text/xml"); 
    } else
        this.rq = new ActiveXObject("Microsoft.XMLHTTP");

    try {
        this.rq.onreadystatechange = connectionEvent;
        this.rq.open("GET", this.source, true);
        this.rq.send(null);
        this.state = 1;
    } catch (err) {
        // some error handler here
    }

}

function connectionEvent() {
    alert("i'm here");
    alert("this doesnt work: " + this.someOtherProperty);
}

} // myClass

so it's nothing more than having the XMLHttpRequest object as a member of my class, instead of globally defined, and invoking it in the traditional way. however, inside my connectionEvent callback function, the meaning of "this" is lost, even though the function itself is scoped inside myClass. i also made sure that the object that i instantiate from myClass is kept alive long enough (declared global in the script).

in all the examples of using javascript classes that i saw, "this" was still available inside the inner functions. for me, it is not, even if i take my function outside and make it a myClass.prototype.connectionEvent. what am i doing wrong? thank you.

+1  A: 

The reason it's not working is that in Javascript, this is defined entirely by how a function is called, not where it's defined. This is different than some other languages.

To have this mean what you expect, you'd have to ensure that explicitly by "binding" it:

this.start = function() {
    var self = this; // Set up something that survives into the closure

    /* ...lots of stuff omitted... */

    this.rq.onreadystatechange = function() {
        // Call `connectionEvent`, setting `self` as `this` within the call
        connnectionEvent.call(self);
    };

There's more information about this management in this blog post, but basically: When a function is called without any particular effort made to set this, this within the function will always be the global object (window, on browsers). There are two ways to set this when making a call:

  1. Using Function#call (or Function#apply) as I did above, passing in the object reference to use as this as the first parameter. That calls the function and sets this to whatever you passed in. The difference between #call and #apply is how you supply further arguments to pass into the function. With #call you supply them as further arguments to the #call call (e.g. func.call(thisArg, arg0, arg1, arg2)), whereas with #apply you supply them as an array in the second argument (func.apply(thisArg, [arg0, arg1, arg2])).
  2. Using dotted notation: If you have an object that has a property with a function assigned to it (like your start property), calling it by using the object instance, a dot, and the property name (this.start() or foo.start(), etc.) will call the function and set this to the object instance within the call. So the dotted notation does two entirely distinct things: Looks up the property and finds a function as its value, and calls the function such that this is set to the object during the call. Literally it's like: var f = obj.func; f.call(obj).

Slightly off-topic, but: Barring a really good reason to, I wouldn't reinvent this wheel. There are lots of libraries out there to simply XHR calls. jQuery, Prototype, Closure, and nearly all the rest.

T.J. Crowder
mind-opening post. thank you so much. (and it works too, hehe.)
Radu M
@Radu: Good deal, glad that helped. (I notice you're new here: If this answered your question, click the check mark to the left of the answer near the top. That marks your question as "answered" and credits this as the right answer.)
T.J. Crowder