These free mini-courses will give you a strong foundation in web development. Track your progress and access advanced courses on HTML/CSS, Ruby and JavaScript for free inside our student portal.
Scroll down...
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
.
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:
myObject.foo()
, it passes in myObject
as this
.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
.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…}
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.
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 ...] )
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 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.
Some good resources to cement the knowledge. We recommend reading this article or taking a look at this book series by Kyle Simpson.
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) }
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.