This (not ‘this’) is a continuation of my previous post on fundamentals.
The keyword ‘this’ can, at times, feel impossible to understand. Before understanding its use and how to figure out what it referred to, I tended to try and avoid it where possible or console log like crazy to try and make sense of it.
Let’s dive in.
‘this’ and the Global Object
When you run your code, a global execution context is created that includes a global object, and a binding for ‘this’. Let’s say we have an empty document and all we add is:
If we run the code and console log whatIsThis, what does it show?
It shows a ‘Window’ object with a bunch of properties and methods attached.
Fun aside, if you open up the ‘Window’ object and scroll way down you’ll find that the ‘Widnow’ object now has a ‘whatIsThis’ property with the value ‘Window’:
As you might imagine, calling ‘this’ by itself in the global scope (that is, not inside a function or method), isn’t all too useful, but it does exist!
You can also now see what’s meant when people say that variables, functions and objects created within the global scope are added to the global object.
‘this’ and Functions/Methods
Above we saw that when you run your code, a global execution context is created with a global object and access to the ‘this’ keyword. Similarly, each time a function is invoked and a new execution context is created, we are given access to the ‘this’ keyword. When I say that we are ‘given access’ I mean that the keyword ‘this’ is not something we ourselves have to declare or create.
When a free function (meaning, it is not a method) is invoked, ‘this’ refers to the global object:
We could log ‘this’ from a function within a function, within a function, and ‘this’ would still refer to the global object:
Why would this still log ‘Window’? Even though the function doing the console log is nested within other functions, it still follows the rule that when a free function is invoked, ‘this’ refers to the global object(‘window’ in your browsers);
Things do change up when we are no longer invoking a free function like above, but instead a method - a function stored within an object as a value.
Methods are invoked using a call time dot (something.method()). When invoking a method, ‘this’ is bound to the object found to the left of the dot at call time. Key word (or phrase really) here is ‘at call time’ - you check what was to the left of the dot when the method was invoked, not where the method was created or passed in as an argument. We’ll get to that later.
Let’s take a look:
Why were we able to change the ‘fruit’ property to “I like bananas”? If we find where the method (conveniently named ‘method’) is invoked, to the left of the call time dot we find ‘a’. So when ‘method’ ran, we were essentially saying a.fruit = ‘I like bananas’. The object ‘a’ being to the left of the call time dot became what the keyword ‘this’ was bound to.
Normally, your code is not as simple as these examples, and you may find functions nested within methods.
This code also logs “I like bananas” - shouldn’t it log “Actually, I like apples”. No, no. This is kind of a frustrating feature, but one that when examined, makes sense. When is apples() invoked? Does it have anything to the right of the call time dot? Does it even have a call time dot? Nope. We previously said that when free functions are invoked, ‘this’ refers to the global object. In fact let’s log the value of ‘this’ within apples() to see what shows up.
There is, however, a way to have ‘this’ refer to object ‘a’ from within apples().
We saw that from within ‘method’, ‘this’ is bound to the ‘a’ object, so we can store that reference in a variable (var that). Because of lexical scoping, apples() will have access to the variable ‘that’ and can now use it within to access and modify the properties on the ‘a’ object.
‘this’ and Constructors
Object constructors can be thought of as functions that create numerous instances of an object with shared methods. An easy tell for identifying object constructors is that the first letter is capitalized.
Let’s say we have an object constructor ‘Phone’:
Now let’s create some phones:
Under the hood, when you call ‘new Phone(…)’ : 1) the keyword ‘this’ is assigned an empty object (first commented out line), 2) properties are assigned to the new object, and 3) the new object is returned.
So, when invoking an object constructor, the keyword ‘this’ refers to an empty object that is created in the background by JS when you use the ‘new’ keyword. It then proceeds to assign the properties - in this case, those properties include memory, color, frontCamera, and backCamera.
You will see the use of constructors quite a lot in JS.
.call() & .apply()
You may or may not have noticed that all functions have access to these three methods - .call(), .apply(), and .bind(). So what are they for?
Were we to invoke greeting() as is, the ‘this’ keyword’ would be pointing to the global object. It would be looking for the property ‘color’ and the property ‘breed’ in the global object. How can we get it so that when we call greeting(), the ‘this’ keyword is bound to the dog object?
Calling dog.greeting() won’t work because greeting() is not a method of dog.
Here is where we can use .call or .apply to help us out. I’m going to invoke greeting as follows:
The first parameter passed to .call is what you want the keyword ‘this’ to get bound to when calling greeting() - in other words, you are passing through the context. The other parameters that follow are the arguments to be passed into the greet() function.
.apply() works exactly the same way except for one difference - it takes the context and an array of arguments:
The .bind() function is a bit trickier than .call() and .apply(). The .bind() function creates an anonymous function that sets the value of the ‘this’ keyword as the first parameter passed in. This may make more sense when we look at some examples.
Let’s say we want to store a method to call at some later point.
What do you expect to be logged?
What gets logged is “Hello, I am a undefined undefined”. When we assign greet to dog.greeting, we are saying:
Do you remember what ‘this’ is bound to within a free function? It is bound to the global object, which does not contain a color or breed property.
So let’s instead try using .bind(). We pass in the object we want ‘this’ to be bound to as the first parameter. Were greeting() itself to accept parameters, we would add these as well within .bind().
This time around we get the result we hoped for. We can now breath easy knowing that when we call boundGreet(), the ‘this’ keyword will be bound to the ‘dog’ object.
But wait! One last question, why do we need to invoke boundGreet? Well, because when we assign boundGreet to greet.bind(dog) we are saying:
Hopefully by now you’re able to see two important unique qualities to .bind():
- .bind() does not immediately invoke a method like .call and .apply
- .bind() creates an anonymous function that assigns the value of the ‘this’ keyword as the first parameter passed in
That was a lot of info so let’s sum it up:
- ‘this’ used on the global object or within a free function is bound to the window at call time
- ‘this’ used inside a constructor is bound to the object created in the background by JS when you use the ‘new’ keyword to instantiate an object
- ‘this’ within methods is bound to what is to the left of the call time dot upon invocation of the method
- .call is used to explicitly bind ‘this’ to an object and pass through arguments
- .apply is used exactly like .call except that you pass the parameters within an array
- .bind creates an anonymous function that assigns the value of ‘this’ as the first parameter passed in