Skip to content

Instantly share code, notes, and snippets.

@drhayes
Last active January 30, 2017 19:36
Show Gist options
  • Select an option

  • Save drhayes/6ea084f169a94e48fb0c40dd3974dc89 to your computer and use it in GitHub Desktop.

Select an option

Save drhayes/6ea084f169a94e48fb0c40dd3974dc89 to your computer and use it in GitHub Desktop.
In which I write about "this"

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:

  1. 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 no this.blah() to call it, it's just blah.

  2. Now I've assigned the value "dogs" to the property "cats" on window. window is the global object when JS is running in a browser. JS always has a global object.

  3. Now I make a new object called "o". o also has a property cats, but this time its value is pants.

  4. First I invoke blah by itself: blah();. Since the call site is not associated with an object, this inside that function refers to the global object window. That means the console is going to log "dogs".

  5. Then I use the function call (a member of the Function.prototype) to set the calling context of the blah function as that object I defined in step 3. Notice that I did not change blah at all, just how I called it. Now it'll log "pants" because the o object has the value "pants" as its cats property.

  6. Then I use the function call to set the calling context to a throwaway object specified as an object literal. That object literal has a property named cats whose value is "horse". That's what is output on the console.

  7. Now I assign the function blah as a property on the object o. That property is named thing. This is where things start getting weirder and where people really start messing up this. Both blah and o.thing refer 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 of this within the function will change.

  8. Proving what I said in step 7 is true, running o.thing(); logs out "pants" because o has a property named cats with the value of "pants".

  9. 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 function blah.

  10. Just like blah, when I call otherThing(); by itself, it logs "dogs" instead of "pants". Even though there's a line saying otherThing = 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment