Подтвердить что ты не робот

Проблема с выпуском setTimeout

У меня есть setTimeout, определяемый внутри функции, которая управляет респауном игрока (я создаю игру):

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

Когда он выполняется, я читаю в консоли "смерть!". и через 3 секунды "живой!". Однако alive никогда не возвращается к истинному, потому что, если я пишу player.alive в консоли, он возвращает false. Почему я вижу "живого!" но переменная никогда не вернется к истине?

4b9b3361

Ответ 1

Вы должны быть осторожны с this. Вам необходимо присвоить свой this во внешней области видимости переменной. Ключевое слово this всегда ссылается на this текущей области, которая изменяется каждый раз, когда вы что-то переносите в function() { ... }.

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

Это должно дать вам больший успех.

Обновление 2019-10-09: Исходный ответ верен, но теперь для последних версий JavaScript доступен еще один вариант. Вместо использования function можно использовать функцию стрелки, которая не изменяет this:

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

Это поддерживается начиная с ES6, который является частью всех современных браузеров, но, думаю, IE (конечно). Если вы используете современный фреймворк для создания своего проекта с помощью Babel или чего-то еще, фреймворк должен убедиться, что он работает везде, как и ожидалось.

Ответ 2

Это потому, что this в обработчике setTimeout ссылается на window, которое, по-видимому, не такое же значение, как указано this вне обработчика.

Вы можете кэшировать внешнее значение и использовать его внутри...

var self = this;

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

... или вы можете использовать ES5 Function.prototype.bind...

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

... хотя, если вы поддерживаете устаревшие реализации, вам нужно добавить прокладку в Function.prototype.


... или если вы работаете в среде ES6...

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

Потому что нет связывания this в Arrow functions.

Ответ 3

На всякий случай кто-нибудь читает это, новый синтаксис javascript позволяет привязать область к функции с "bind":

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

Ответ 4

Вероятно, потому что this не сохраняется в обратном вызове с таймаутом. Попробуйте:

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

Обновить (2017) - или использовать функцию лямбда, которая будет неявно захватывать this:

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

Ответ 5

С синтаксисом функции ES6 область "this" не изменяется внутри setTimeout:

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