link to codepen if you want to follow along
The 'this' keyword in JavaScript can be a confusing topic for a lot of new developers. In fact, it's a question I happened to get wrong in a job interview and as a result I've put some effort into studying and understanding it (don't want to make the same mistake twice). This blog post will go through the 4 principles of 'this' and how each one is applied in JavaScript.
Let's start by talking about what 'this' means in english. If I were to tell you "Hey! Look at this" - what is this? You really have no idea what 'this' refers to unless I give you some context. If I pick up an object and point to it and say "look at this" you will quickly be able to understand that 'this' refers to the object in which I am pointing to. It's the same in JavaScript. If we give this no context at all it will return the window to us, the global object in node and undefined in strict mode. This brings us to our first principle of 'this'
1. Window Binding
Window binding is not something we aim to use. It's what happens when we do not give context for the 'this' keyword. If we don't tell JavaScript what 'this' is it will return the window to us, the global object in node or undefined in strict mode.
Here's an example:
function sound(){
console.log(this.bark);
}
sound() // we will get back the window in the console
This brings us to our next principle:
2. Implicit binding
Implicit binding is probably the most common principle of 'this', it apply to objects with methods and it says when the function is invoked, look to the left of the dot. That's what 'this' refers to.
Let's see an example:
const ada = {
name: 'Ada',
breed: 'Bali dog',
bark: 'woof woof'
sound: function(){
console.log(this.bark);
}
}
ada.sound(); // invoking the function. This bark refers to ada's bark because ada is the left of the dot when the function is invoked
A good way to remember implicit binding is that it's IMPLIED that whatever is to the left of the dot when the function is invoked that's what 'this' will refer to.
3. Explicit Binding
With explicit binding we explicitly pass in as an argument what we want 'this' to refer to. We do that using .call(), .apply(), or .bind()
. There are some differences with how we use these.
.call()
- will immediately invoke the function, with .call you pass in the arguments 1 by 1
.apply()
- will immediately invoke the function, with .apply you would pass in the arguments as an array
.bind()
- you will pass in your arguments 1 by 1 but it does not immediately invoke the function, instead it returns a brand new function that can be invoked later.
Let's see an example of .call
:
function sound(){
console.log(this.bark);
}
const ada = {
name: 'Ada',
breed: 'Bali Dog',
bark: 'woof woof'
}
sound.call(ada); // invoking the function here and binding this bark to Ada's bark - we will get back woof woof in the console.
Let's see an example of .bind
:
function sound(){
console.log(this.bark);
}
const ada = {
name: 'Ada',
breed: 'Bali Dog',
bark: 'woof woof'
}
const goodDog = sound.bind(ada); // creating a new function called goodDog that can be invoked later
goodDog(); // we will get back bark bark in the console
4. New Binding
New binding is used with constructor functions. It says that when a function is invoked as a constructor function using the 'new' keyword 'this' points to the newly created object. At this point you may be asking "what is a constructor function?" Great question! A constructor function constructs other objects, that's it's whole life's purpose. Some things you may notice about a constructor function is that it has a capitalized function name, there is an assignment of the 'this' keyword and it may be missing a return statement.
Let's see an Example:
function Pet(name, breed, bark){
this.name = name;
this.breed = breed;
this.bark = bark;
}
Pet.prototype.sound = function(){ //here we are using the prototype keyword to abstract out the sound method so that we can pass it across newly created objects without if affecting memory
console.log(this.bark);
}
const ada = new Pet('Ada', 'Bali Dog', 'woof woof'); // creating my new object - this.name will be 'Ada' this.breed will be 'Bali Dog' and this.bark will be 'woof woof' - 'this' is pointing to my newly created object which is ada.
ada.sound()// will log 'woof woof'
One final call out, do not use arrow functions inside of object methods. Arrow functions don't bind this 'this' keyword, they pass it through. This is not a bug, it's feature of arrow functions. For more on that Wes Bos wrote a really great post called Arrow Function No No's linked here
I hope this was helpful for you! Let me know in the comments <3
Top comments (6)
Hi Brit,
can you explain the implicit binding a bit more? I had situations where this wouldn't function, but I couldn't find out why (not); in particular with stacked calls.
Apart from that - very helpful! :-)
All the best,
Sebastian.
Hey Sebastian, Thanks for your comment. Could you give me an example of where implicit binding didn't work for you?
Hi Brit,
sorry for the late response - I couldn't exactly remember where it happened, but now I found the problem:
Assume the following micro-example:
Here's a possible corresponding html-file:
You'll notice that JS throws a TypeError which must have something to do with the scope, I presume. I've usually found workarounds, but I do not like them. I'd like to really understand the problem.
Thanks!
Sebastian.
This is likely a scope issue. A couple of things to know about classes:
In your case point 1 is the issue - classes are not hoisted. You've created Handler below where you are invoking it.
Here's a codepen where I explain classes a little deeper. Hopefully that's helpful :)
codepen.io/BritHemming/pen/WNvYJMr
Thanks for your effort ;)
this. Article is :
/s 😁