The this
keyword is one of the most widely used features of JavaScript. It is without doubt one of the hardest things to grasp. This blog post aims to help you understand the value of this
in different contexts.
First and foremost, we should stop trying to make the this
keyword in JavaScript like the this
keyword in other languages such as C++, Java, and PHP. There, the this
keyword represents the instance of the current object and it only makes sense inside a method of a class. You cannot use it outside of the method.
When it comes to JavaScript, this
can be used both in the global context and function context.
In the global context, this
references the global object, which is the window object on the web browser or the global object on Node.js.
For example:
this.dog = 'German Shepherd';
console.log(window.dog); // 'German Shepherd'
In functions, this
references the execution context of that function call. It is determined completely by how the function is called.
This may seem unusual for a lexically scoped language like JavaScript but, it is not possible to simply look at a function's definition and tell what the this
keyword points to. What it points to depends on
how the function was called.
Let's see all the ways that a function can be called and see how the this
keyword behaves.
4 ways to call a function
Implicit Binding
Implicit Binding is applied when you invoke a function in an object using the dot notation.
let dog = { breed: "Poodle", getBreed() { return console.log(this.breed); }, }; dog.getBreed()
dog.getBreed()
says:invoke
getBreed()
with thethis
keyword pointing at thedog
objectImplicit Binding is very useful for sharing behavior between different objects.
function getBreed() { return console.log(this.breed); } let dog = { breed: "Poodle", getBreed, }; let cat = { breed: "Birman", getBreed, }; dog.getBreed(); //'Poodle' cat.getBreed(); //'Birman'
Notice how dog and cat are two different objects with different data, but they are sharing the same
getBreed()
function. There are no two differentgetBreed()
functions in memory, there is just one.Explicit binding
You can invoke a function with the
call()
method (orapply()
) and you can provide as a first argument a value forthis
.let dog = { breed: "Pug", }; getBreed.call(dog); //'Pug'
What
getBreed.call(dog)
is basically saying is:invoke
getBreed
function, with a particular context that I am going to specify as first argument (dog).A variation of explicit binding is called hard binding.
You can usebind()
, to make a function that, no matter how it is called, it has a particularthis
value.NOTE:
bind()
doesn't invoke the function, it just creates a new one which is bound to a particular this context.Let's see the following code:
const dog = { breed: "Poodle", getBreed() { return console.log(this.breed); }, }; const myDog = dog.getBreed; myDog(); //'undefined' const myBoundDog = dog.getBreed.bind(dog); myBoundDog(); //'Poodle'
myDog()
printsundefined
becausethis
refers to the global context. AndmyBoundDog()
prints Poodle becausedog.getBreed.bind(dog)
returns a new function bound to dog.The new keyword
The
new
keyword is the third way to invoke a function. It's purpose is to call a function with thethis
keyword pointing to a new empty object.function Dog(breed){ this.breed = breed } const myDog = new Dog("Bulldog")
First it creates a new empty object
{}
. It uses that empty object as value forthis
. It sets a breed property with the value "Bulldog" and returns that object.Default binding
This one is just a simple function invocation.
const breed = "Bulldog" function getBreed() { return console.log(this.breed); } getBreed() // 'Bulldog' (in non-strict mode)
const breed = "Bulldog" function getBreed() { "use-strict" return console.log(this.breed); } getBreed() // TypeError
The reason why in strict mode we get a type error is because in strict mode, if you invoke a function without any
this
value (implicit binding, explicit binding or with the new keyword), the default behaviour is to leave it asundefined
.
And accessing a property on theundefined
value throws the Type Error. Which is good because calling a this aware function without anythis
is probably a mistake.
Arrow Functions
Arrow functions do not define a this
keyword no matter how are invoked. That means the this
keyword inside an arrow function, behaves the same way as any other variable, which means it is going to lexically resolve to the nearest enclosing scope, that has defined a this
keyword.
We can verify that it doesn't define a this
keyword simply by trying to invoke an arrow function with the new
keyword:
const myArroyFn=(breed)=>{this.breed=breed}
new myArroyFn("Poodle") // 'TypeError'