JS:如何将上下文传递给访问函数类的setTimeOut匿名函数 箭头功能静态捕获范围:功能也可以手动绑定到特定的this:

问题描述


这是我第一次在Stack Overflow上发帖;我只有足够的知识可以玩Javascript,但是我编写的所有内容都有错误。拜托,如果我的用词不对,请原谅我。

我刚刚了解到setTimeOut可用于创建多个计时器。因此,我编写了一个函数类来使用自己的计时器创建对象。但是,当我通过该函数类的方法调用setTimeout()并传递一个匿名函数时,它似乎没有适当的上下文。我该如何解决

我已经通过在函数类中编写要执行的函数解决此问题,但这却失去了它的灵活性。

下面是我(不太漂亮)的工作代码段:

函数类声明:

function sal_clock2 () {
 this.initialized = false;
 var aTimerObject = {timerStarted: false};
 this.holdTimers = [];
 var mythis = this; 
 this.checkIfnotinit = function(index) {
    if (typeof this.holdTimers[index] === "undefined") { //if timer doesn't exist then create it.
      this.holdTimers[index] = {timerStarted: false};
    }
};
 this.setTimeout = function(runFunction,millisecondsDelay) { 
/*runFunction is what I want it to do. Right Now it only takes an array index.*/
    //I need the right context so I provide custom "this" by means of "mythis"
    var IDofTimer = setTimeout(function(){mythis.holdTimers[runFunction].timerStarted = false;},millisecondsDelay);
    return IDofTimer; //returning IDofTimer so that I can reference it later to stop timer if necessary.
}; //end this.setTimeOut

}

sal_clock2类在尝试设置“敌人对玩家造成伤害”间隔时的用途:

var t = new sal_clock2();


for (i = 0; i < $dataMap.events.length; i++) {//do loop to find all enemies so they can attack
if ($dataMap.events[i]) { 
    if ($dataMap.events[i].Meta.enemy == "1") { //do enemy attack
        if ($gameMap._events[i]._characterName !== ""){
        if ($gamePlayer._y-1 == $gameMap._events[i]._y && $gamePlayer._x == $gameMap._events[i]._x || // If x is same,and above enemy
            $gamePlayer._y+1 == $gameMap._events[i]._y && $gamePlayer._x == $gameMap._events[i]._x || // If x is same,and below enemy
            $gamePlayer._x-1 == $gameMap._events[i]._x && $gamePlayer._y == $gameMap._events[i]._y || // If y is same,and left of enemy
            $gamePlayer._x+1 == $gameMap._events[i]._x && $gamePlayer._y == $gameMap._events[i]._y) { // If y is same,and right of enemy
                if (!$gameMap.event(i)._sal_unconscIoUs) {//if enemy is not "dead"              

                t.checkIfnotinit(i); //this seems to successfully setup timer for use.

                    if (t.holdTimers[i].timerStarted == false) {
                        t.holdTimers[i].timerStarted = true;
                        t.setTimeout(i,3000); //**//this is my custom setTimeout**
                        //t.setTimeout calls a function defined in function class constructor "sal_clock2",//which calls JS's setTimeout()
                        $gameActors._data[1]._hp -= 20;
                        $gamePlayer.requestAnimation(1);
                    } //end 
                } //end sal_unconscIoUs check
            } //end coordinate check
        }
    }//end if 
}

}

首选代码调用(与t.setTimeout(i,3000)相对的 ):

t.setTimeoutTest(function(){t.holdTimers[i].timerStarted = false;},3000);

我的问题是,与其在函数类构造函数中进行函数声明,不如在这里进行上述冲突“ for循环”。但是传递匿名函数并不能让我访问 t.holdTimers [i] .timerStarted = true; ,这样我就可以防止通过requestAnimationFrame重复执行该函数,而只能按指定的时间间隔进行重复。

请帮助,
无知的编码器

修改
很抱歉,我不清楚。我上传了一些与冲突代码分开的代码,并将其发布在github上。它仅具有输出价值的基础,一个有效的例子,一个无效的例子。在此处查看:github

我在匿名函数上尝试了.bind(this),但没有成功。将对象引用“ t”传递给匿名函数也是不成功的。我什至无法使用命名函数

解决方法

您在错误的范围内设置了mythis。与其他语言(例如Java和C ++)一样,无法在定义它们的函数范围之外访问javascript中的局部变量。因此,请在正确的范围内定义mythis

function sal_clock2 () {
 this.initialized = false;
 var aTimerObject = {timerStarted: false};
 this.holdTimers = [];
 // var mythis = this; // <----- WRONG PLACE,remove this
 this.checkIfNotInit = function(index) {
    if (typeof this.holdTimers[index] === "undefined") { //if timer doesn't exist then create it.
      this.holdTimers[index] = {timerStarted: false};
    }
};


this.setTimeout = function(runFunction,millisecondsDelay) { 

    var mythis = this; // <----- you need to capture `this` HERE

    var IDofTimer = setTimeout(function(){mythis.holdTimers[runFunction].timerStarted = false;},millisecondsDelay);
    return IDofTimer; //returning IDofTimer so that I can reference it later to stop timer if necessary.
}; //end this.setTimeOut

但是,现代javascript具有可用于避免this别名/缓存的功能:

箭头功能静态捕获范围:

this.setTimeout = function(runFunction,millisecondsDelay) { 

    // PASS AN ARROW FUNCTION to setTimeout:

    var IDofTimer = setTimeout(() => {
        this.holdTimers[runFunction].timerStarted = false; // no need mythis
    },millisecondsDelay);

    return IDofTimer;

};

功能也可以手动绑定到特定的this

this.setTimeout = function(runFunction,millisecondsDelay) {

    var callback = function () {
        this.holdTimers[runFunction].timerStarted = false; // no need mythis
    }

    // Manually binding the callback function allow you to pass any `this`
    // you want to it:
    var IDofTimer = setTimeout(callback.bind(this),millisecondsDelay);

    return IDofTimer;

};
,

感谢那些对我的编码问题有深刻见解的人,我得出了我需要在代码中修复的内容。我对范围,范围和绑定的理解需要一些调整。

首先,我的匿名函数需要使用“ this”。

t.setTimeoutTest(function() {this.holdTimers[i].timerStarted = false;},3000);

这似乎误导了我,因为“ this”的上下文不正确,直到我在函数类的setTimetOutTest函数中尝试以下操作为止:

this.setTimeoutTest = function(runFunction,millisecondsDelay) {
     var IDofTimer = setTimeout(runFunction.bind(this),millisecondsDelay);
}

如果这是结束的话,我将把上面的答案标记为正确。但是我不是很幸运。

使用此代码会导致错误,导致我的应用崩溃。我终于意识到,我的for循环编程了7次迭代,但是错误在8日崩溃。在我的非专家看来,似乎是第7次迭代完成后,便将索引增加到了第8次。这是一个问题,因为我使用索引来引用变量,但是到t.setTimeout()调用该函数时,变量已经增加到数组中的未定义元素,导致代码崩溃。

我通过将索引保存到变量并在我的匿名函数中使用它来解决了这个问题。

var myI = i;
t.setTimeoutTest(function() {this.holdTimers[myI].timerStarted = false;},3000);

现在,我的代码可以按预期工作并且更加健壮,尽管有点不干净。谢谢大家!