Skip to content

Instantly share code, notes, and snippets.

@latentflip
Last active August 29, 2015 14:10
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save latentflip/55c78ead831e23d5ec59 to your computer and use it in GitHub Desktop.
Save latentflip/55c78ead831e23d5ec59 to your computer and use it in GitHub Desktop.

this

Plain functions

Let's start with a plain old function:

var myFunc = function () { console.log(this); }

If we run that function we will see that this is window/global (depending on whether you are in the browser or in node:

myFunc() // Window{...}

So window/global is the default this of a function call. Nothing fancy there.

With an object

Okay, what about if we have an object with a function (method):

x = {
  aFunc: function () { console.log('this: ', this); }
};

If we run this, we'll see that this, is x

x.aFunc() // this: x{...}

Which makes sense, right? It's a function on x, so of course this is x. So why? Is it something special about the function, well no. Look what happens when we do this:

var someFunc = x.aFunc; //grab aFunc off x and stick it in a variable
someFunc() // this: Window{...}

this is window again? Wtf? It turns out this is defined at the moment you call the function, not when you create the object. So it can change really easily. So how do you know what this will be? It's actually really easy: look at what's on the left of the function call. That's what will be this. If there's nothing, it'll be window. Let's try a few examples:

    var x = {
      aFunc: function () { console.log('this:', this);
    };

    x.aFunc() // this: x{...}
//  ^-- x is on the left, so this is x

    var someFunc = x.aFunc; //save aFunc to a variable
    someFunc()              // this: Window{...}
// ^- there's nothing on the left, so this is window

    var y = {};
    y.aFunc = x.aFunc;      //steal aFunc from x and attach it to y
    y.aFunc()               //this: y{...}
//  ^-y is on the left, so this is y

Capiche?

Callbacks

When functions are used as callbacks, the exact same principles apply, it can just be a bit harder to follow. Here's an example:

setTimeout takes a function and a delay, and calls the function after a set amount of time:

var x = {
  aFunc: function () { console.log('this:', this); }
};
setTimeout(x.aFunc, 1000);

So what will this be here? It looks like it should be x, right, because x is to the left of aFunc? But it's not, it's window again!

Why? Well, remember, this will be thing on the left of the function when the function is called. We aren't calling the function here, we are passing it into setTimeout as an argument, and setTimeout will call it later. Which is basically the same as saving the function off to a variable and calling it later.

Let's demonstrate by writing a function which takes a callback and calls it:

var x = {
  aFunc: function () { console.log('this:', this); }
};

var takesACallback: function (callback) {
  callback();
//^- uh, oh. nothing on the left here...
}

takesACallback(x.aFunc); // this: Window{...}

There are ways to fix this, but we need to learn about call, apply and bind quickly.

call and apply

Call and apply are basically the same, they just accept arguments slightly differently. They both allow you to specify the this inside a function, by passing the object you want to be "this" as the first argument.

var myFunc = function () { console.log('this:', this); }
var x = {};
var y = {};

myFunc() // this: Window{...}
myFunc.call(x) //this: x{...}
myFunc.apply(x) //this: x{...}

myFunc.call(y) //this: y{...}
myFunc.apply(y) //this: y{...}

So call and apply allow you to set the this that you want inside the function. What's the difference? Well if your function takes arguments, you need a way to pass them in. Call let's you pass them as normal. Apply lets you pass them as an array. that's the only difference:

var x = {};
var myFunc = function (a, b) { console.log('this:', this, 'a:', a, 'b:', b); };
myFunc(1,2) // this: Window{...} a:1 b:2
myFunc.call(x,1,2) // this: x{...} a:1 b:2
myFunc.apply(x, [1,2]) // this: x{...} a:1 b:2
              //^---^ note we're passing an array with apply

binding

Function bindings allows us to set this on a function for anytime it's called in the future no matter how without calling the function. Let's just look at an example:

var x = {
  myFunc: function () { console.log('this:', this); }
};

var someFunc = x.myFunc.bind(x);

someFunc() // this: x{...}

Woah! So even though there's nothing on the left of someFunc, it's this is still x. That's the power of bind. Note that bind doesn't call the function, it just returns a new function, with this bound to something.

This is really useful for our callbacks example. Remember this:

var x = {
  aFunc: function () { console.log('this:', this); }
};
setTimeout(x.aFunc, 1000); // this: Window{...}

Because we passed the function to setTimeout, when it gets called later, this is Window, which kinda sucks, as we probably wanted it to be x. One way to fix this is using bind:

setTimeout(x.aFunc.bind(x), 1000);
@niallobrien
Copy link

A great explanation, thank you.

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