views:

137

answers:

2

Hi all,

I am trying to figure out the right place to bind a function prototype to be called later. The full code of the example can be found here:

http://www.iprosites.com/jso/

My javascript example is very basic:

function Obj(width, height){
    this.width = width;
    this.height = height;
}

Obj.prototype.test = function(){
    var xhr=init();
    xhr.open('GET', '?ajax=test', true);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
    xhr.onreadystatechange = function() {
        if (xhr.responseText == '403') {
            window.location.reload(false);
        }
        if (xhr.readyState == 4 && xhr.status == 200) {
            this.response = parseResponse(xhr.responseText);
            document.getElementById('resp').innerHTML = this.response.return_value;
            this.doAnotherAction();
        }
    };
    xhr.send();
}

Obj.prototype.doAnotherAction = function(){
    alert('Another Action Done');
}


var myo = new Obj(4, 6);

If you try to run myo.test() in Firebug, you will get the "this.doAnotherAction is not a function" response. The 2 support functions init() and parseResponse() can be found in the test.js link if you wish to view them, but should not be too relevant to this problem. I've affirmed that this.doAnotherAction() thinks "this" is the XMLHttpResponse object as expected from an instanceof test.

Can anyone help with some insight on direction with binding? Everything I've tried seems not to work!

I do use Mootools, although the library is not present in this example.

Thanks in advance,

Arion

+1  A: 

this inside of xhr.onreadystatechange does not refer to the instance of Obj. you need to capture this outside of the function in a local variable an then use that inside of xhr.onreadystatechange. i.e.

Obj.prototype.test = function(){
    var obj = this,
        xhr=init();
    xhr.open('GET', '?ajax=test', true);
    xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
    xhr.onreadystatechange = function() {
        if (xhr.responseText == '403') {
            window.location.reload(false);
        }
        if (xhr.readyState == 4 && xhr.status == 200) {
            this.response = parseResponse(xhr.responseText);
            document.getElementById('resp').innerHTML = this.response.return_value;
            obj.doAnotherAction();
        }
    };
    xhr.send();
}

Test this on your test page by pasting the above into the console, then running myo.test() :)

Russ Cam
well son of a gun, it does work when I do that. It just seems a little jerry-rigged though to avoid the proper usage of function binding though, doesn't it? I'm thankful that you have provided a workable answer, but are there any thoughts on the nature of binding in this case by chance? Thanks a ton!!! :)
Arion
what do you mean by the proper usage of function binding? The function context inside the aonymous function assigned to `onreadystatechange` (i.e. `this`) is the `xmlHttpRequest` object. Without applying the function with the context of the `Obj` instance (as in Anurag's answer), I don't think there's anyway around that. So you need to capture the outer context in a variable and use that variable inside of the inner function.
Russ Cam
+1  A: 

ECMAScript 5th ed. has borrowed the bind method on functions from the Prototype framework. You can include it in your code which will define a bind method if it's not already present.

if (!Function.prototype.bind)
{
    Function.prototype.bind = function() {
        var fn = this, 
            args = Array.prototype.slice.call(arguments), 
            object = args.shift();

        return function() {
            return fn.apply(object,
                args.concat(Array.prototype.slice.call(arguments)));
        };
    };
}

Once defined, you can bind the anonymous function inside the onreadystatechange callback with the object of Obj.

xhr.onreadystatechange = function() {
    ...
}.bind(this);
Anurag
ahhh ok so my problem before was that I was trying to add the bind(this) onto the test() or doAnotherAction() methods, when I should have bound "this" to the the onreadystatechange callback.
Arion