setTimeout 范围问题

IT技术 javascript
2021-03-14 02:29:59

我在控制玩家重生的函数内部定义了一个 setTimeout(我正在创建一个游戏):

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

当它执行时,我在控制台中读到,“死亡!” 3 秒后“活着!”。但是,alive从未真正设置回 true,因为如果我player.alive在控制台中写入,它会返回false. 我怎么能看到“活着”!但变量永远不会设置回true?

5个回答

你必须小心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 月 9 日更新:最初的答案是正确的,但现在为最新版本的 JavaScript 提供了另一种选择。除了使用function,您可以使用箭头函数代替,它不会修改this

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

这是从 ES6 向前支持的,我认为这是所有当前浏览器的一部分,但 IE(当然)。如果您使用现代框架通过 Babel 或其他方式构建您的项目,该框架应确保它在任何地方都能按预期工作。

如需额外的功劳,请阅读 javascript 的函数.call().apply()方法,这有助于理解thisjavascript 中的工作原理,并且在您需要在传递函数之外的其他地方定义函数时会派上用场。jQuery.proxy()和 Prototype.bind()为这个功能提供了简洁的包装。
2021-05-03 02:29:59
除了@nnnnnn 所说的,如果您使用的是 ES6,则可以使用箭头函数。() => setTimeout( ()=> this.alive = true, 3000)
2021-05-14 02:29:59
除了@BrianMortenson 所说的,JS 有自己的.bind()方法(在 1.8.5 版中引入,因此在较旧的 IE 中不支持)。实际上,该 MDN 页面上的示例之一是用于setTimeout().
2021-05-15 02:29:59

这是因为thissetTimeout处理程序中引用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);

因为没有thisinArrow functions绑定

以防万一有人读到这里,新的 javascript 语法允许您使用“bind”将作用域绑定到函数:

window.setTimeout(this.doSomething.bind(this), 1000);
不幸的是,像这样直接绑定到函数似乎不起作用(至少,在 Chrome 60 中不是)。似乎您必须首先通过匿名函数运行它,如上面的“社区维基”ES5 示例。
2021-05-21 02:29:59

可能是因为this没有保留在超时回调中。尝试:

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

更新 (2017) - 或使用 lambda 函数,它将隐式捕获this

var timer3 = setTimeout(() => {
    this.alive = true;
    ...
@Kokodoko 我同意!这是在 2012 年回答的。我会更新答案。
2021-04-24 02:29:59
以我的拙见,that = this不应再使用解决方案,因为它只会增加由this关键字引起的混乱最好你应该使用箭头符号setInterval( () => {})或者bind()用来修复 javascript 的范围问题。
2021-05-04 02:29:59

使用 ES6 函数语法,'this' 的作用域不会在 setTimeout 内改变:

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