Domanda problema di ambito setTimeout


Ho un setTimeout definito all'interno di una funzione che controlla il respawn del giocatore (sto creando un gioco):

var player = {
    ...
    death:(function() {
        this.alive = false;
        Console.log("death!");
        var timer3 = setTimeout((function() {
            this.alive = true;
            Console.log("alive!");
        }),3000);
    }),
    ...
}

Quando si esegue, ho letto nella console, "morte!" e 3 secondi dopo "vivo!". Però, alive non è mai veramente impostato su true, perché se scrivo player.alive nella console, ritorna false. Come mai posso vedere "vivo!" ma la variabile non è mai impostata su true?


22
2017-07-30 01:18


origine


risposte:


Devi stare attento con this. Devi assegnare il tuo this nello scope esterno a una variabile. Il this parola chiave sempre si riferisce a this dello scope corrente, che cambia ogni volta che avvolgi qualcosa function() { ... }.

var thing = this;
thing.alive = false;
Console.log("death!");
var timer3 = setTimeout((function() {
    thing.alive = true;
    Console.log("alive!");
}),3000);

Questo dovrebbe darti un successo migliore.


26
2017-07-30 01:21



È perchè this nel setTimeout gestore si riferisce a window, che presumibilmente non è lo stesso valore di cui fa riferimento this fuori dal conduttore.

Puoi memorizzare nella cache il valore esterno e usarlo all'interno ...

var self = this;

var timer3 = setTimeout((function() {
    self.alive = true;
    Console.log("alive!");
}),3000);

... oppure puoi usare ES5 Function.prototype.bind...

var timer3 = setTimeout((function() {
    this.alive = true;
    Console.log("alive!");
}.bind(this)),3000);

... sebbene tu stia supportando le implementazioni legacy, dovrai aggiungere uno shim a Function.prototype.


... o se lavori in un ambiente ES6 ...

var timer3 = setTimeout(()=>{
    this.alive = true;
    Console.log("alive!");
},3000);

Perché c'è nessun legame di this in Arrow functions.


12
2018-02-22 14:45



Nel caso qualcuno lo legga, la nuova sintassi javascript ti consente di associare un scope a una funzione con "bind":

window.setTimeout(this.doSomething.bind(this), 1000);

7
2017-07-30 01:21



Probabilmente perché this non viene mantenuto nel callback di timeout. Provare:

var that = this;
...
var timer3 = setTimeout(function() {
    that.alive = true;
    ...

Aggiornamento (2017) - o utilizzare una funzione lambda, che acquisirà implicitamente this:

var timer3 = setTimeout(() => {
    this.alive = true;
    ...

4
2017-08-03 21:20



Con la sintassi della funzione ES6, l'ambito per "questo" non cambia all'interno di setTimeout:

var timer3 = setTimeout((() => {
    this.alive = true;
    console.log("alive!");
}), 3000);

3