2013-09-26 70 views
2

我是JavaScript新手。因此這個問題有點混亂。我試圖簡單地定義一個計數器並在類方法中增加它,但它不像我預期的那樣運行。具體console.log(this.tick_count);打印undefinedJavaScript中的類變量返回undefined

的JavaScript:

function Game() { 
    this.fps = 50; 
    this.ticks = 3; 
    this.current_time = (new Date).getTime(); 
    this.draw_object = document.getElementById('game_canvas').getContext('2d'); 
    this.tick_count = 0; 
} 

Game.prototype.update = function (time) { 
    this.current_time = time; 
} 

Game.prototype.draw = function() { 
    this.draw_object.fillRect(10, 10, 55, 50); 
} 

Game.prototype.run = function() { 
    self.setInterval(this.tick, 1000/(this.fps * this.tick)); 
} 

Game.prototype.tick = function() { 
    this.tick_count++; 
    console.log(this.tick_count); 
} 

function start_game() { 
    var game_object = new Game(); 
    game_object.run(); 
} 

HTML:

<body onload="start_game()"> 
    <canvas id="game_canvas" width="1024" height="1024"></canvas> 
</body> 

從Python的背景,我覺得這種行爲奇怪的到來。我應該如何正確設置我的類變量?

+2

你在哪裏定義'self'?而'setInterval'內的'this'並不是你想象的那樣。 – Mathletics

+0

[Javascript setInterval範圍問題]的可能的重複(http://stackoverflow.com/questions/11333311/javascript-setinterval-scoping-issue) – Mathletics

+1

嘗試'self.setInterval(this.tick.bind(this),1000 /( this.fps * this.tick));'看看它是怎麼回事 – PSL

回答

7

這是發生了什麼。

基本上,您的tick函數不再運行在game_object對象的上下文中。這聽起來可能來自Python背景,但基本上this對象被設置爲其他東西。

那麼它是如何設置的?容易,window對象,我們怎麼知道這個?因爲setInterval的上下文是window對象。

移動例如作爲代碼將無法格式化正確下面

綁定實例

setInterval(this.tick.bind(this), 1000/(this.fps * this.tick)); //Native (JS v1.8+) 
$.proxy(this.tick, this); //jQuery 
_.bind(this.tick, this); //underscore/lodash 

顯式上下文例如

Game.prototype.run = function() { 
    var _this = this; 
    setInterval(function() { 
     //So what is this at the moment? window. 
     //Luckily we have a reference to the old this. 
     _this.tick(); 
    }, 1000/(this.fps * this.tick)); 
}; 

可以解決這個問題有兩種方式。

  1. 綁定你的功能,你希望它是在Bind JS v1.8(看到,因爲你正在使用的畫布,不應該是一個問題的對象。
  2. 與它的上下文明確調用的方法。(見上文)
+0

老兄,是的,我爲此瘋狂了,謝謝 – Dagrooms

3

嘗試

setInterval(this.tick.bind(this), 1000/(this.fps * this.tick)); 
// without "self" 

由於PSL和TJ克羅德

+2

我認爲這裏的調用不正確,它會調用該函數並將結果設置爲setInterval的回調。你應該使用綁定。 – PSL

+0

嗯..不知道,我測試 –

+1

不,setTimeout(this.tick.call(this),...'會**立即調用**'tick',並將返回值傳遞給'setTimeout'。 –

2

這將工作:

setInterval(this.tick.bind(this), 1000/(this.fps * this.tick)); 

本作將會:

var self = this; 
setInterval(function() { 
    self.tick(); 
}, 1000/(this.fps * this.tick)); 
0

儘管已經得到解答,但我認爲您需要了解this指的是什麼。有關更多詳細信息,請參閱this answer

如果您想使用閉包而不是綁定,則可以通過調用返回函數的函數(在當前正在運行的函數之外)來限制範圍。這樣可以最大限度地減少可用於閉包的變量數量。聽起來很複雜,但你可以做一些小的調整:

Game.prototype.run = function() { 
    //best not to define a closure here because the next variable 
    //will be available to it and won't go out of scope when run 
    //is finished 
    var memoryEatingVar=new Array(1000000).join("hello world");; 
    //note that this.tick(this) is not passing this.tick, it's invoking 
    //it and the return value of this.tick is used as the callback 
    //for the interval 
    setInterval(this.tick(this), 1000/(this.fps * this.tick)); 
} 
//note the "me" variable, because the outer function Game.prototype.tick 
//returns a inner function (=closure) the me variable is available in 
//the inner function even after the outer function is finished 
Game.prototype.tick = function (me) {//function returning a function 
    return function(){ 
    me.tick_count++; 
    console.log(me.tick_count); 
    } 
}