我试图用Java语言包扎头,想制作一个简单的Web小程序,该小程序可以通过按钮启用和禁用,并且在启用时会绘制东西。为了编写更干净的代码,我想为此使用一个对象。使用按钮的页面设置为
<div>
<input type="button" name="runbutton" value="Run" onclick="game.run();"/>
<input type="button" name="resetbutton" value="Reset" onclick="game.reset();"/>
</div>
<script>
//...
</script>
并且javascript代码是
function Game() {
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('updating... runs:', this.runs);};
};
var game = new Game();
game.reset();
setInterval(game.update, 300);
因此,这是一个对象定义(游戏),其中一个实例(游戏)具有一个布尔属性(运行)和三个方法。一个运行它,一个停止运行,以及一个update()方法报告它是否运行。使用setInterval每隔300 ms重复执行一次update()。
问题:控制台从update()日志中报告this.runs的值未定义,而不是false或true。当我打开控制台并将其暂停以检查变量时,它将把game.runs正确报告为false或true。另外,当我将console.log()调用添加到run()并reset()来报告this.runs的值,然后再对其进行设置之后,似乎可以正确地报告是非。因此,问题似乎出在update()中。好像使用了错误的“ this”。也许setInterval不能在方法上使用?
我为代码尝试了另外两种语法,但是它们似乎有完全相同的问题:
var game = {
runs: false,
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();
setInterval(game.update, 300);
以及在对象内设置setInterval的版本:
var game = {
runs: false,
i: undefined,
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.runs报告为未定义?我是否正确认为方法中的“ this”在所有情况下确实均指游戏实例?我不应该在方法上使用setInterval而是调用全局函数吗?
答案 0 :(得分:1)
在JavaScript中,this
的规则有些复杂;相关的是,如果作为方法调用,则存储在对象属性中的非箭头函数可以将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
。由于this
是定义功能的游戏对象,因此将它们变成箭头功能将始终将this
设置为该游戏对象。这需要在定义时进行更改,而不是在调用时进行更改:
this.update = () => {console.log('updating... runs:', this.runs);};
答案 1 :(得分:0)
当您使用以下语法定义内部函数时:function() {}
,则此函数将具有其自己的this
,因此,如果希望将此this.runs
用作对象的对象,则function Game() {
this.runs = false;
this.run = () => {console.log('run...'); this.runs = true;};
this.reset = () => {console.log('reset...'); this.runs = false;};
this.update = () => {console.log('updating... runs:', this.runs);};
};
var game = new Game();
game.reset();
setInterval(game.update, 300);
将是未定义的。父函数,您有两个选择:
选项1:将内部函数定义为箭头函数:
<div>
<input type="button" name="runbutton" value="Run" onclick="game.run();"/>
<input type="button" name="resetbutton" value="Reset" onclick="game.reset();"/>
</div>
this
选项2::将父函数function Game() {
self = this
this.runs = false;
this.run = function() {console.log('run...'); self.runs = true;};
this.reset = function() {console.log('reset...'); self.runs = false;};
this.update = function() {console.log('updating... runs:', self.runs);};
};
var game = new Game();
game.reset();
setInterval(game.update, 300);
存储为变量
<div>
<input type="button" name="runbutton" value="Run" onclick="game.run();"/>
<input type="button" name="resetbutton" value="Reset" onclick="game.reset();"/>
</div>
declare
pl_query varchar2(4000);
cl_collectie constant varchar2(255) := 'MY_COLLECTION';
begin
--
if apex_collection.collection_exists( p_collection_name => cl_collectie )
then
apex_collection.delete_collection( p_collection_name => cl_collectie );
end if;
--
pl_query := q'[select QUERY]' ;
--
apex_collection.create_collection_from_queryb2( p_collection_name => cl_collectie
, p_query => pl_query );
end;
答案 2 :(得分:0)
由于this
的上下文不再是game
,因此game.update
被称为setInterval
的回调,如果使用箭头功能将解决此问题。 / p>
function Game() {
self = this
this.runs = false;
this.run = function() {console.log('run...'); self.runs = true;};
this.reset = function() {console.log('reset...'); self.runs = false;};
// Using arrow function () => {} instead of normal function
this.update = () => {console.log('updating... runs:', self.runs);};
};
var game = new Game();
game.reset();
setInterval(game.update, 300);
详细了解箭头功能和 this here