简体   繁体   English

进行OO javascript回调时的范围

[英]scope when doing OO javascript callbacks

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... 伙计们 - 我正在努力学习如何编写OO Javascript,我来自as3 OO背景...我遇到的问题是将一个类'方法作为回调传递给另一个类...

in the example below, i create an instance of AppController class, and in it, i create an instance of ConnectionMonitor class. 在下面的示例中,我创建了一个AppController类的实例,在其中,我创建了一个ConnectionMonitor类的实例。 I pass one of AppController's methods to be called back by ConnectionMonitor. 我传递一个AppController的方法,由ConnectionMonitor调用。 The calling back works fine, but seems that what's inside the callback function looses the scope of the class its in (AppController )... 回调工作正常,但似乎回调函数内部的内容失去了它的范围(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());
}

Modify init to bind the callback to the original context: 修改init以将回调绑定到原始上下文:

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: 不幸的是, bind是不是在所有的浏览器都支持 ,但是你可以捕捉的当前值this在一个封闭来达到类似的效果:

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: 另一种方法是使detectInitialConnection能够处理可选的上下文参数:

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. 这些示例中的每一个中的要点是从调用detectInitialConnection的上下文中保留对this的值的引用。

setTimeout will run the passed function bound to window . setTimeout将运行绑定到window的传递函数。 You can override this with ES5's bind : 您可以使用ES5的bind覆盖它:

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

This doesn't work in older browsers, but the provided MDN link has a workaround. 这在旧版浏览器中不起作用,但提供的MDN链接有一个解决方法。

this is tricky in javascript especially as it relates to constructors and passing functions around. 这在javascript中很棘手,特别是因为它与构造函数和传递函数有关。 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");

}

}

this.initialConnectionDetected will pass the function, but without the information that it should be executed on the instance. this.initialConnectionDetected将传递该函数,但没有它应该在实例上执行的信息。

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). 调用this.foo()将传递此信息,但仅在直接调用时,而不是在传递并稍后调用时(这是您正在执行的操作)。

You have to bind this information (ie the this value, which is the instance) using eg .bind : 您必须使用例如.bind绑定此信息(即this值,即实例):

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. 请注意, .bind在旧版浏览器中不可用,但有可用的填充程序模仿它的行为。

The other answers are correct, but nobody seems to mention that Function.prototype.bind is not supported in all browsers . 其他答案是正确的,但似乎没有人提到所有浏览器都不支持 Function.prototype.bind To make it work, you'll want to include Kris Kowal's es5-shim library . 为了使它成功,你需要包括Kris Kowal的es5-shim库

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM