I have a problem in regard to setInterval
that I can't figure out.
There is the problem with the scope when calling setInterval
or timeout from within an object, but still I can't wrap my head around it.
I tried to put my stuff inside an anonymous function, it won't work.
This is basicly my problem, simplified to the bare bones:
function Scenario(){
var ships = [];
this.ini = function(){
for (var i = 0; i < ships.length; i++){
timeoutID1 = setTimeout(ships[i].ding, 1000);
timeoutID2 = setTimeout(ships[i].bing, 1000);
}
}
this.setShips = function(){
var ship = new Ship("ship");
ships.push(ship);
}
function Ship(name){
this.name = name;
this.ding = function(){
intervalID1 = setInterval(function(){
console.log("ding");
}, 500)
}
this.bing = function(){
var _this = this;
intervalID2 = setInterval(function(){
console.log(_this.name);
}, 500)
}
}
this.setShips();
}
var scenario = new Scenario();
scenario.ini();
http://jsfiddle.net/ancientsion/xkwsn7xd/
Basicly, console.log("ding")
works, console.log(_this.name)
doesn't.
Why?
By the time setTimeout()
gets around to call your method, it only sees the function and not the invocation context (i.e. the object to bind it to); much like this:
var bing = ships[i].bing;
bing(); // inside bing(), this == window
Basically, you need to provide setTimeout()
with a prewired method invocation:
var bound_bing = ships[i].bing.bind(ships[i]);
timeoutID2 = setTimeout(bound_bing, 1000);
The "magic" happens with .bind()
as it returns a new function that will have this
set up properly.
This is your problem simplified to bare bones:
var ship = {
name: 'Sheep',
ding: function() {
console.log(this.name);
}
}
setTimeout(ship.ding, 1000); // doesn't work correctly
It may help to see another example to understand why the above doesn't work:
var ding = ship.ding;
ding(); // doesn't work either
In JavaScript this
depends on how you call your function. ship.ding()
will set this
to the sheep
object. Bare ding()
call will set this
to the window
object.
You can bind the function to the object you want by using .bind()
method. (Function.prototype.bind())
var ding = ship.ding.bind(ship);
ding(); // works
ding
is now permanently bound to the sheep
object. You can use exactly the same approach with setTimeout
:
setTimeout(ship.ding.bind(ship), 1000);
You should define the value _this
in Ship
function:
function Ship(name){
this.name = name;
this.ding = function(){
intervalID1 = setInterval(function(){
console.log("ding");
}, 500)
}
var _this = this;
this.bing = function(){
intervalID2 = setInterval(function(){
console.log(_this.name);
}, 500)
}
}
Hoist _this out of the bing function.
you should have
_this = this,
this.bing = function() {
intervalID2 = setInterval(function(){
console.log(_this.name);
}, 500)
}
Javascript is imperative so you need to follow the execution path carefully. In function Ship, this points to a Ship object, in function bing, this points to the global scope (window). You need to save a reference to this so that you can refer back to it in these types of functions.
You put ships[i].bing
in setTimeout turns out that the caller of bing is not ships[i] but global, so _this
is pointing to global actually.