Basic Syntax: This

What the `this` key word is and how to use it in its various contexts.

Scroll down...

Content

Resources

Comments

Because JavaScript functions get passed around all over the place and are potentially executed in a different time and context than they were called, it makes sense that they need to know who called them. A function's caller is saved to the variable this and represents a hidden argument that gets passed to every function when it is executed. Much of the trickiness and power of JavaScript comes from understanding this.

The Role of "This"

this is a special keyword which represents the object on which the current method is being invoked.

As we've said, functions get passed around all the time in JavaScript. They may finally get executed in a number of different places, so you can never really be sure who's going to be calling it. That makes the value for this a particularly interesting (and useful) bit of information.

One mental trick to use is to imagine this as an extra parameter on every function call, one you normally don't have to set manually. Unless you use call or apply functions (which we'll look at in a sec), it defaults to whatever is left of that dot in myObject.foo(). If there's nothing left of the dot, the implicit window global object is standing by to be this.

Why play this mind game? Because many beginners in JavaScript mistakenly believe that this depends on "where a function lives in the code." They try to look to the text of the function itself, or to the object where that text is stored, to tell them what this will be. That's a problem because it's how and where a function is called that matters. Sometimes, one object can inherit another object's functions; other times, popular JavaScript libraries actively take a function and assign this to a new object using call or apply.

Long story short: never count on this to be something certain until each specific time the function is actually triggered.

To recap:

  1. When you call a function defined on an object like myObject.foo(), it passes in myObject as this.
  2. When you define a function out in the global namespace (with no parent object) like function foo() { ... };, there's still an implicit parent object, the global Window object. That means that when you call foo(), the value of this will turn out to be window.
  3. We'll look at modifying this with call and apply in a sec...

First, see the first two examples in action:

// let's output `this` to see who's calling
// first declaring a function globally here
function foo(){
  console.log( this );
}


// Let's declare the same function as property
// of an Object so we can play with `this`
var myObj = {
  myFunc: foo
}


// Now call `foo` "method style" on the object and it
// logs `this` as myObj (with its myFunc function attached)
myObj.myFunc();
//=> Object {myFunc: function}


// If we just call `foo` "function style" in the global
// namespace, it will log the global Window object
foo();
//=> Window {top: Window, window: Window, location: Location, external: Object, chrome: Object…}


// For fun, what if we call `foo` inside a `forEach`?
// ...it's still setting `this` to the global Window.
[1,2,3].forEach( foo );
// Window {top: Window, window: Window, location: Location, external: Object, chrome: Object…}
// Window {top: Window, window: Window, location: Location, external: Object, chrome: Object…}
// Window {top: Window, window: Window, location: Location, external: Object, chrome: Object…}

Using 'Call' and 'Apply' to set 'This'

If you use call or apply, both functions which let you run another function, you can manually set the calling object. In other words, you can change what this is.

We said above that you can think of this as an invisible argument that gets passed to every function. This is a situation where that mental model pays off: when using call and apply, it turns into a visible first parameter before the other arguments, and you can set it as you please. You can even set it as undefined, if there's nowhere you need its value in the function.

Let's look at using call and apply to run a function and explicitly set its this:

// Use `call` on `foo` without passing a specific
// caller and we get back the global object as well
foo.call();
//=> Window {top: Window, window: Window, location: Location, external: Object, chrome: Object…}


// `apply` accomplishes the same thing
foo.apply();
//=> Window {top: Window, window: Window, location: Location, external: Object, chrome: Object…}


// Now let's set up a random object to be
// our caller in the next example
someOtherObj = { "hello": "world" };


// Now pass that object as the caller (`this`)
// by making it the first argument to `.call()`
foo.call(someOtherObj);
//=> Object {hello: "world"}


// Cool!  We told `foo` it was called by `someOtherObj`
// even though we didn't explicitly do that.
// This is conceptually equivalent to the following
// BUT the following doesn't actually work since
// we haven't set up `foo` as a property on `someOtherObj`
// ... so `call` lets you do things you normally can't.
someOtherObj.foo();
//=> Uncaught TypeError: undefined is not a function

It may seem odd why you want to explicitly pass in the object who is supposedly invoking your function to call or apply (therefore "binding" your function to that object), but file that away for future knowledge. It will come up again.

Again, much of this has to do with the fact that functions may be called asynchronously or they may be called when nested many levels deep inside other functions. In that case, it's very important who is actually calling a function, and therefore what this is. In later lessons, we'll see more ways to ensure that your function is properly bound to the right object and scope.

'Call' vs 'Apply'

There is practically no difference between using call or apply. You can use either one. In simple cases (like the examples above), we tend to lean towards call because it resembles other languages.

The only difference is that when you also need to pass arguments to the function you're calling, call takes them as a list of arguments ("C" is for "Comma-separated-arguments") while apply wants them packaged up into an array ("A" is for "Array of arguments"):

// `call` takes additional arguments as a list
// (remember the first arg is always what you
// want `this` to be)
foo.call( valueOfThis, arg1, arg2, arg3 ...)


// `apply` takes additional arguments as an array
foo.apply( valueOfthis, [ arg1, arg2, arg3 ...] )

ES6 Moment: Arrow Functions

To avoid some of the edge cases and trauma associated with this, ES6 gives us the arrow function. First let's take a look at what it looks like, contrasted against the ES5 version.

// The way you are used to
array.forEach(function(element) {
  console.log(element * 2)
})

// The ES6 way, using an arrow function
array.forEach(element => {
  console.log(element * 2)
})

Wow! We saved 8 characters! Your wrists will thank you. Admittedly, this is not a huge savings. But similarly to ES6’s string interpolation, I think you'll find that it makes your code slightly easier to read.

Luckily, a slightly terse or syntax is not the only benefit of arrow functions. Let's look at this piece of code which may not behave exactly as you might expect.

let junkBag ={
  junk: [1,"gold",true],
  treasure: [],
  sortJunk: function() {
    this.junk.forEach(function (item, index) {
      if (item == "gold") {
        this.junk.splice(index, 1)
        this.treasure.push(item)
      }
    })
  }
}

junkBag.sortJunk()
console.log(junkBag.treasure)
console.log(junkBag.junk)

If you try to run that code, you will get the error, TypeError: undefined is not an object (evaluating 'this.junk.splice'). That is because junk is undefined and you cannot access a splice property on undefined. But why does this not have a junk property? It turns out that the function passed to forEach is executed in the context of the global window object. You can see this for yourself by replacing the contents of the inner function with console.log(this). So how do we maintain our current context; how do we make it so that our this refers to our junkBag? Can you guess?

You got it! We use an arrow function! Try this:

let junkBag ={
  junk: [1,"gold",true],
  treasure: [],
  sortJunk: function() {
    // Here comes the arrow function.
    // Notice that we need an extra set
    // of parentheses now that we have
    // two arguments.
    this.junk.forEach((item, index) => {
      if (item == "gold") {
        this.junk.splice(index, 1)
        this.treasure.push(item)
      }
    })
  }
}

junkBag.sortJunk()
console.log(junkBag.treasure)
console.log(junkBag.junk)

The code now works as expected. Any instances of the special keyword this inside of an arrow function will be a “lexical this.” You can think of it more of an “intuitive this.” So if you ever have any difficulty with this in your code, consider changing its enclosing function to an ES6 arrow function. To see how you might solve this problem in ES5 JavaScript, check this out.

More Resources

Some good resources to cement the knowledge. We recommend reading this article or taking a look at this book series by Kyle Simpson.

Code Review

The important bits of code from this lesson

// return the calling object with `this`
function foo(){ return this; }

// call naked
foo();                        //=> Window ...

// store in an object then call
bar = { myFoo: foo }
bar.myFoo();                  //=> Object { myFoo: function }

// pass in an explicit caller to bind it to
foo.call(bar);                //=> Object { myFoo: function }

// ES6 Arrow Function syntax
var baz = (boop) => { console.log(boop) }

Wrapping Up

There is much power in understanding the calling context (this) and how to use functions as constructors for objects. These properties together give you the ability to pass functions around with confidence and use them to create complex object-oriented programs. Their usage won't settle in fully until you've had numerous chances to play with them directly, but hopefully you got a chance to become familiar with them along the way. Spend some time in the console playing with some of these examples if you're feeling confused.



Sign up to track your progress for free

There are ( ) additional resources for this lesson. Check them out!

There are no additional resources for this lesson just yet!

Sorry, comments aren't active just yet!

Next Lesson: Running JavaScript from Files