0

コールバックで「this」をバインドしないと、setTimeout でコンテキストが失われるのはなぜですか。
以下のコードでは、onTime が 3 つの場所で呼び出されていることがわかります。
クラスの中から一度。これはうまくいきます。
クラスの外から一度。これはうまくいきます。
タイマーがコールをトリガーしたときに 1 回。「this」には bind メソッドが必要です。
すべてのコールバックでバインドを使用せずにコンテキストを強制する方法はありますか?

var MyBaseClass = function () {
    "use strict";
    this._base = {};
    this._base.baseFunc = function () {
        console.log('baseFunc called')
    };
    return this._base;
};

var TestClass =  function () {
    "use strict";
    if (TestClass.prototype._singletonInstance) {
        return TestClass.prototype._singletonInstance;
    }
    TestClass.prototype._singletonInstance = this;
    this._timer = {};
    console.log('constructor')
};
TestClass.prototype = new MyBaseClass();        // Set prototype to MyBaseClass
TestClass.prototype.constructor = TestClass;   // Set constructor back to TestClass

TestClass.prototype.onTime = function () {
    "use strict";
    console.log('ontime called')
    console.log(this, "<--- why do i lose context")
    this.baseFunc()
}
TestClass.prototype.init = function () {
    "use strict";
    console.log('local')
    this.onTime()
    //this._timer = setTimeout(this.onTime.bind(this),500)// works
    this._timer = setTimeout(this.onTime, 500) // no work
};

var asdf = new TestClass()
asdf.init()
console.log('global')
asdf.onTime()
4

2 に答える 2

1

Notice that you only pass a function to setTimeout, not an object, so setTimeout could not possibly be aware of your desired context. The solution is to use a wrapping function to hold the context (dojo's hitch, known as bind in many other libs). This is the way to force context, I'm not sure why you're looking for something else. One option could be to bind once in a constructor, then you could pass the function around without worrying about context, like this :

this.onTime = this.onTime.bind(this); // now you can pass the function around this._timer = setTimeout(this.onTime, 500) // works

To clarify and to try and deal with your question about prototypes...

The reason why you would bind the function in the constructor (which is what I assumed your init function would be used as) as opposed to anywhere else, is that if you construct (using new) a second object, you want that object to be the context for the functions executed on it. You get this behaviour for free when executing the functions directly (myObj.myFunc) but care is needed when the functions are passed on (setTimeout(myObj.myFunc, 50) or var callback = myObj.myFunc;). This is the core problem you are experiencing and there are two strategies you can use :

Bind when passing the function

This is basically the method you developed yourself, when passing your function to setTimeout, you bind it to your object. I'm still not sure I understand why you don't like this technique, but it is a valid pattern which is used by many libs etc.

setTimeout(myObj.myFunc.bind(myObj), 50);

Bind when constructing the object

This is the soonest possible time you could bind - you cannot bind when setting the functions on the prototype, because the object has not been constructed yet, what would you bind to? With this technique, you can call setTimeout normally, safe in the knowledge that the function you are passing has been "prebound".

var myObjClass = function(){
    this.myFunc = this.myFunc.bind(this);
}
myObjClass.prototype.myFunc = function(){};
var myObj = new myObjClass();

setTimeout(myObj.myFunc);
于 2013-03-15T15:59:22.690 に答える
0

JSFiddle

コンテキストを維持するには、setTimeout で新しいメソッドを作成します。コンテキスト関連付けのないメソッド参照を渡しています。

TestClass.prototype.init = function () {
    "use strict";
    console.log('local')
    this.onTime();
    var me = this; 
    //this._timer = setTimeout(this.onTime.bind(this),500)// works
    this._timer = setTimeout(function(){
        me.onTime();
        }, 500) 
};
于 2013-03-15T15:53:54.680 に答える