Reputation: 23
I'm trying to develop a small game and I have the following problem: I have a pseudo class "Cannon", each Cannon has an array that stores the areas it should guard and an array that stores "intruders" that have entered one of those guarded areas. I created the next function as part of Cannon's prototype:
Cannon.prototype.checkIntruderLeftGuardedAreas = function(){
debugger;
this.intruders = this.intruders.filter(function(intruder){
for(var i = 0, l = this.guardedAreas.length; i < l; i ++)
{
if(intruder.inInsideGuardedArea(this.guardedAreas[i]))
{
return true;
}
}
return false;
});
}
On the third line I'm trying to filter those intruders that have left the guarded areas, but "this" references to "window" instead of the Cannon objetc that invoked the function.
I also tried this code:
var intrudersArray = [];
for(var i = 0, l = this.guardedAreas.length; i < l; i ++)
{
for(var j = 0, k = this.intruders.length; j < k; j++)
{
if(this.intruders[j].inInsideGuardedArea(this.guardedAreas[i]))
{
intrudersArray.push(this.intruders[j]);
}
}
}
this.intruders = intrudersArray;
This, according to my logic, does the same thing than the previous block of code: filter the array of intruders. The problem is that on the 6th line where the "if" begins, suddenly "this" references to "window"!. Before reaching that if condition, "this" references to the Cannon object that called the function.
Upvotes: 2
Views: 498
Reputation: 82306
When you call checkIntruderLeftGuardedAreas, bind the "this" object from the callee.
obj.checkIntruderLeftGuardedAreas.bind(this)(arg1, arg2, ..., argn);
In JavaScript, this is a context, not the current class.
Example: Define a function that outputs "this"
function lol(){console.log(this);}
Now call that function
lol();
and you get window.
Furthermore, define a function, and call it in an object:
var obj = { abc: function(){lol(); }}
Then
obj.abc();
will still get you window.
You need
var obj = { abc: function(){lol.bind(this)(); }}
And the this context will change to obj, when you call the function
obj.abc();
You'd probably have to do this:
Cannon.prototype.checkIntruderLeftGuardedAreas = function(){
debugger;
this.intruders = this.intruders.filter.bind(this)(function(intruder){
for(var i = 0, l = this.guardedAreas.length; i < l; i ++)
{
if(intruder.inInsideGuardedArea(this.guardedAreas[i]))
{
return true;
}
}
return false;
});
}
Notice the "bind" after filter
Upvotes: 1
Reputation: 3630
Yep, scoping can get tricky, you could try the following:
Cannon.prototype.checkIntruderLeftGuardedAreas = function(){
var self = this;
self.intruders = self.intruders.filter(function(intruder){
for(var i = 0, l = self.guardedAreas.length; i < l; i ++)
{
if(intruder.inInsideGuardedArea(self.guardedAreas[i]))
{
return true;
}
}
return false;
});
}
Notice the line var self = this;
. It's purpose is saving a reference to your cannon instance to use inside nested callbacks (note how this
is now self
inside filter
) in order to avoid scope issues, it's common to see such stuff in jQuery code.
This practice ensures you reference your cannon without the need to track what's going on with this
, self
like that is obvious at first glance.
Upvotes: 2