In JavaScript, this refers to the calling context of the function. Unlike other more traditional languages, in JS it is only possible to know the value of this within a function by examining, at runtime, what called the function. Let's look at some examples:
function blah() { console.log(this.cats); }
window.cats = 'dogs';
var o = { cats: 'pants' };
blah(); // console logs "dogs"
blah.call(o); // console logs "pants"
blah.call({ cats: 'horse' }); // console logs horse
o.thing = blah;
o.thing(); // console logs "pants"
var otherThing = o.thing;
otherThing(); // console logs "dogs"
Let's take it one-by-one:
-
First we define a function that logs the value "this.cats". Notice that this function is sitting by itself in the global namespace as
blah. There's nothis.blah()to call it, it's justblah. -
Now I've assigned the value "dogs" to the property "cats" on window.
windowis the global object when JS is running in a browser. JS always has a global object. -
Now I make a new object called "o".
oalso has a propertycats, but this time its value ispants. -
First I invoke
blahby itself:blah();. Since the call site is not associated with an object,thisinside that function refers to the global objectwindow. That means the console is going to log "dogs". -
Then I use the function
call(a member of theFunction.prototype) to set the calling context of theblahfunction as that object I defined in step 3. Notice that I did not changeblahat all, just how I called it. Now it'll log "pants" because theoobject has the value "pants" as itscatsproperty. -
Then I use the function
callto set the calling context to a throwaway object specified as an object literal. That object literal has a property namedcatswhose value is "horse". That's what is output on the console. -
Now I assign the function
blahas a property on the objecto. That property is namedthing. This is where things start getting weirder and where people really start messing upthis. Bothblahando.thingrefer to the same function, the same area in memory. But since the call site is different (blah();in one,o.thing();in the other) the value ofthiswithin the function will change. -
Proving what I said in step 7 is true, running
o.thing();logs out "pants" becauseohas a property namedcatswith the value of "pants". -
Hammering the point home, I make a new name, "otherThing", that points to
o.thing. This is now an alias, another name, of the original functionblah. -
Just like
blah, when I callotherThing();by itself, it logs "dogs" instead of "pants". Even though there's a line sayingotherThing = o.thing;JS will not use "o" as the context for this function anymore. In Python, this function would be called unbounded.
Again, just to hammer this home, if you typed "blah === otherThing" the JS console would say true. If you said "blah === o.thing" it would also say true. The function isn't changing at all, only its call-site. That's the only thing that matters to functions in JS when figuring out what the value of this is.
If you hand a function to a Phaser.Signal like this: onEvent.add(this.blah); you are stripping the call context from that function. The signal will not do what you probably expect. You can fix that two ways: you can use bind or you can pass the listener context.
In Phaser you usually pass the context. For signals, that looks like this: onEvent.add(this.blah, this); Notice that second argument is this? I've just told Phaser what context it should use when that signal fires.
You could also do this: onEvent.add(this.blah.bind(this)); In Phaser you usually don't have to do this, but if you did you are creating an entirely new function that is forever bound to the value of this as it stood at runtime at this moment.
To return to the earlier example, "otherThing === o.thing === blah", right? But if I made one called var boundThing = blah.bind({ cats: 'poop'}); then I have made an entirely new function (i.e. "blah !== boundThing") that is bound to that object with the value "poop" for the property named cats.
This concept is fundamental to working successfully in JavaScript. It will come up again and again and trips up even very experienced developers.