2

Folks - I'm trying to learn how to write OO Javascript, I come from as3 OO background... issue I'm having is with passing a class' method as a callback to another class...

in the example below, i create an instance of AppController class, and in it, i create an instance of ConnectionMonitor class. I pass one of AppController's methods to be called back by ConnectionMonitor. The calling back works fine, but seems that what's inside the callback function looses the scope of the class its in (AppController )...

any thoughts?

//in the HTML
<script>
    $(document).ready( function(){
        new AppContoller().init();
    });
</script>


//in the js file

//AppController Class
var AppContoller = function(){
    this.body = $("body");

    this.init = function(){
        this.connection = new ConnectionMonitor();
        this.connection.detectInitialConnection( this.initialConnectionDetected );
    }

    //callback function I pass
    this.initialConnectionDetected = function(bool){
        if(bool){
            trace("app connected? "+bool); // logs - "app connected? true"
            this.showOnlineSwf();  //thows error - "Object [object DOMWindow] has no method 'showOnline' "
        }
        else{

        }
    }

    this.showOnlineSwf = function(){
        trace("i'm online");

    }

}

//ConnectionMonitor Class
var ConnectionMonitor = function()
{   
    this.detectInitialConnection = function(callbackFunction){
        setTimeout(callbackFunction, 1000, [true]);
    }
}




function trace(arg){
    console.log(arg.toString());
}

5 Answers 5

4

Modify init to bind the callback to the original context:

this.init = function() {
    this.connection.detectInitialConnection(
        this.initialConnectionDetected.bind(this));
}

Unfortunately, bind is not supported in all browsers, but you can capture the current value of this in a closure to achieve a similar effect:

this.init = function() {
    var that = this;
    this.connection.detectInitialConnection(function(detected) {
        that.initialConnectionDetected(detected);
    });
}

Yet another approach is to make detectInitialConnection capable of handling an optional context parameter:

this.detectInitialConnection = function(callbackFunction, _this){
    setTimeout(function() {
        callbackFunction.apply(_this || this, arguments);
    }, 1000, [true]);
}

You'd then call it like this:

this.init = function() {
    this.connection.detectInitialConnection(
        this.initialConnectionDetected, this);
}

The point in each of these examples is to retain a reference to the value of this from the context in which detectInitialConnection was called.

Sign up to request clarification or add additional context in comments.

Comments

2

setTimeout will run the passed function bound to window. You can override this with ES5's bind:

this.connection.detectInitialConnection( this.initialConnectionDetected.bind(this) );

This doesn't work in older browsers, but the provided MDN link has a workaround.

2 Comments

thanks, that works in chrome but not in FF 3.6 ?? FF says "his.initialConnectionDetected.bind is not a function [Break On This Error] this.connection.detectInitialConnect...tialConnectionDetected.bind(this) ); "
It's available starting in FF4, there is a compatability table in the article. You can include the code clip MDN provides or google for "ES5 shim".
2

this is tricky in javascript especially as it relates to constructors and passing functions around. If you do this it will work:

//AppController Class
var AppContoller = function(){
this.body = $("body");
var self = this;
this.init = function(){
    this.connection = new ConnectionMonitor();
    this.connection.detectInitialConnection( this.initialConnectionDetected );
}

//callback function I pass
this.initialConnectionDetected = function(bool){
    if(bool){
        trace("app connected? "+bool); // logs - "app connected? true"
        self.showOnlineSwf();
    }
    else{

    }
}

this.showOnlineSwf = function(){
    trace("i'm online");

}

}

Comments

1

The other answers are correct, but nobody seems to mention that Function.prototype.bind is not supported in all browsers. To make it work, you'll want to include Kris Kowal's es5-shim library.

Comments

1

this.initialConnectionDetected will pass the function, but without the information that it should be executed on the instance.

Calling this.foo() will pass this information, but only when called directly, not when passed around and called later (which is what you're doing).

You have to bind this information (i.e. the this value, which is the instance) using e.g. .bind:

this.connection.detectInitialConnection(this.initialConnectionDetected.bind(this));

Note that .bind is not available in older browsers, but there are shims available which mimic the behaviour of it.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.