用setInterval调用的方法将Javascript对象属性报告为未定义
我正试着用Javascript来概括我的想法,我想制作一个简单的web小程序,它可以通过按钮来启用和禁用,并且在启用时,可以绘制一些东西。为了编写干净的(er)代码,我想为此使用一个对象。带有按钮的页面设置为用setInterval调用的方法将Javascript对象属性报告为未定义,javascript,methods,this,setinterval,Javascript,Methods,This,Setinterval,我正试着用Javascript来概括我的想法,我想制作一个简单的web小程序,它可以通过按钮来启用和禁用,并且在启用时,可以绘制一些东西。为了编写干净的(er)代码,我想为此使用一个对象。带有按钮的页面设置为 //... javascript代码是 函数游戏(){ this.runs=false; this.run=function(){console.log('run…');this.runs=true;}; this.reset=function(){console.log('reset
//...
javascript代码是
函数游戏(){
this.runs=false;
this.run=function(){console.log('run…');this.runs=true;};
this.reset=function(){console.log('reset…');this.runs=false;};
this.update=function(){console.log('update…runs:',this.runs);};
};
var game=新游戏();
game.reset();
设置间隔(game.update,300);
所以它是一个对象定义(游戏),一个实例(游戏)有一个布尔属性(运行)和三个方法。一个运行它,一个停止运行,还有一个update()方法报告它是否运行。使用setInterval每隔300毫秒重复更新()
问题:控制台从update()日志中报告this.runs的值未定义,而不是false或true。当我打开控制台并暂停它以检查变量时,它会正确地报告game.runs为false或true。另外,当我向run()和reset()添加console.log()调用时,会在设置之前和之后报告this.runs的值,它似乎会正确地报告true和false。所以问题似乎出在update()中的某个地方。就好像它用错了“this”。可能无法在方法上使用setInterval
我为代码尝试了另外两种语法,但它们似乎有完全相同的问题:
var游戏={
跑步:错,
run:function(){console.log('run…');this.runs=true;},
reset:function(){console.log('reset…');this.runs=false;},
update:function(){console.log('update…runs:',this.runs);}
};
game.reset();
设置间隔(game.update,300);
以及在对象内设置setInterval的版本:
var游戏={
跑步:错,
i:未定义,
run:function(){console.log('run…');this.runs=true;this.i=setInterval(this.update,300);},
reset:function(){console.log('reset…');this.runs=false;clearInterval(this.i);},
update:function(){console.log('update…runs:',this.runs);}
};
game.reset();
同样的问题
发生了什么事?为什么update()会将此运行报告为未定义?在所有情况下,方法中的“this”确实指的是游戏实例,这是正确的吗?我是否应该在方法上使用setInterval,而不是调用全局函数?当您使用以下语法定义内部函数时:
function(){}
,则此函数将有自己的this
,因此this.runs
将是未定义的,如果您希望它成为父函数的对象,您有两个选择:
选项1:将内部函数定义为箭头函数:
函数游戏(){
this.runs=false;
this.run=()=>{console.log('run…');this.runs=true;};
this.reset=()=>{console.log('reset…');this.runs=false;};
this.update=()=>{console.log('update…runs:',this.runs);};
};
var game=新游戏();
game.reset();
设置间隔(game.update,300)代码>
因为此
的上下文不再是游戏
,game.update
被调用为对setInterval
的回调,如果使用箭头函数,它将解决问题
函数游戏(){
self=这个
this.runs=false;
this.run=function(){console.log('run…');self.runs=true;};
this.reset=function(){console.log('reset…');self.runs=false;};
//使用arrow函数()=>{}代替普通函数
this.update=()=>{console.log('update…runs:',self.runs);};
};
var game=新游戏();
game.reset();
设置间隔(game.update,300)代码>在JavaScript中,此
的规则有些复杂;相关的是,如果作为方法调用,则存储在对象属性中的非箭头函数可以将this
分配给对象。让我们分析一下:
game.update
是game
对象的属性,✅李>
- 它包含一个非箭头函数,✅李>
- 它被作为一个方法调用。。。❌李>
“作为方法调用”是什么意思?这意味着您在object.property
语法上调用函数,如下所示:game.update(…)
但是,game.update
作为一个参数传递,它将失去与game
的连接。您的代码相当于:
var func = game.update;
setInterval(func, 300);
其中setTimeout
将只调用func()
。这意味着game.update
是作为函数调用的,而不是作为方法调用的,This
在调用时不会设置为game
典型的解决办法是:
- 将接收器绑定到函数。这是在上述方法调用之外设置
This
的另一种方法:如果函数绑定到接收方对象,则在调用时它将始终将This
设置到该对象。你可以这样写:
setInterval(game.update.bind(game), 300)
React中经常使用的一种变体是在定义位置将函数显式绑定到接收器:
this.update = function() {console.log('updating... runs:', this.runs);};
this.update = this.update.bind(this);
- 通过以下任一方式显式使用方法调用:
setInterval(() => game.update(), 300);
setInterval(function() { game.update(); }, 300);
- 让
通过使用箭头函数对该进行词汇定义。由于此
是定义函数时的游戏对象,因此将其设置为箭头函数将始终将此
设置为该游戏对象。这需要在定义点而不是调用点进行更改:
this.update = () => {console.log('updating... runs:', this.runs);};