Understanding the this keyword in JavaScript

Posted by

In JavaScript, the This keyword refers to an object that is executing the current piece of code. This is one of the most important but yet a bit confusing topics in JavaScript. In this tutorial, the following topics will be covered

  • Implicit Binding
  • Explicit Binding
    • Call
    • Apply
    • Bind
  • How does this work with the New keyword?
  • Window Object
  • How does this work with Arrow function?

We will cover these topics in the tutorial and at the end of this, you will be able to understand This keyword in JavaScript completely.

Implicit Binding

The first rule of This keyword in JavaScript is implicit binding. This rule will cover most of the usage of This keyword. We already know that This keyword only deals with the Object and we can access the Object’s property with a dot(.) notation. The implicit binding follows two rules.

The first rule is to find out where the function has been called and the second one is to check whether there is any dot notation before that function or not. If there’s any then the left side of the dot notation Object will become This. Let’s see the following example to understand it more clearly.

const Student = {
    name : 'Rahul',
    age : 25,
    printStudentName : function (){
        console.log(this.name)
    } 
}

Student.printStudentName()

// Output: Rahul

This is a basic example of implicit binding. But how do we know that? Remember! the two rules of implicit binding?

Here, you can see that we have called the function at the end of the student object. Besides, there’s a dot notation before that function and it indicates an Object named Student. In this case, This which is used in the printStudentName method is mainly indicating the Student Object itself by following the implicit binding rules. Follow the below code example to verify this statement.

const Student = {
    name : 'Rahul',
    age : 25,
    printStudentName : function (){
        console.log(this)
        console.log(this.name)
    } 
}

Student.printStudentName()

/* 
Output:
{
  name: 'Rahul',
  age: 25,
  printStudentName: [Function: printStudentName]
}
Rahul
*/

Here, you can see that we have printed This first and in the output, it shows all of the properties of the Student object. As we know, we can access any properties of an Object by using the dot(.) notation. We access the name of the Student object by applying this.name.

Explicit Binding

This keyword is used in JavaScript to reuse a function on the basis of a context. When a function is declared on the global scope and needs to use inside an Object, we can not perform it by following explicit binding. Explicit binding is nothing but defining an Object as This keyword manually. JavaScript provides three functions to perform this action and these are- call(), apply(), and bind(). Let’s see an example of call() in the below section:

Call() Function

const printStudentName = function (v1, v2, v3){
    console.log(this)
    console.log(`${this.name} is ${v1}, ${v2} and ${v3}`)
} 

const Student = {
    name : 'Rahul',
    age : 25
}

const v1 = 'Brilliant'
const v2 = 'Attentive'
const v3 = 'Industrious'

printStudentName.call(Student, v1, v2, v3)

/*
{ name: 'Rahul', age: 25 }
Rahul is Brilliant, Attentive and Industrious
*/

Here, you can see that we are defining which Object will be referred to as This keyword explicitly with the help of the call() function. Inside the parameter of the call function, we pass the Object name and some other parameter as the virtue of the student. As a result, we can easily access all the properties of this Object by using This keyword inside the printStudentName() function.

Apply() Function

The call() function does not able to take an Array along with the Object as the parameter. But what if you need to pass an array? Here, apply() function comes to play its role. See the below code example:

const printStudentName = function (v1, v2, v3){
    console.log(this)
    console.log(`${this.name} is ${v1}, ${v2}, and ${v3}`)
} 

const Student = {
    name : 'Rahul',
    age : 25
}

const v1 = 'Brilliant'
const v2 = 'Attentive'
const v3 = 'Industrious'

const arr = [v1, v2, v3]

printStudentName.apply(Student, arr)

/*
{ name: 'Rahul', age: 25 }
Rahul is Brilliant, Attentive, and Industrious
*/

Here, you can see that this function behaves exactly the same as the call() function. But it gives us an extra feature to pass an array as the parameter.

Bind() Function

The bind() function behaves exactly like the call() function but the only difference is it creates an instance of the function instead of calling the function itself. Follow the below code example to understand it more clearly.

const printStudentName = function (v1, v2, v3){
    console.log(this)
    console.log(`${this.name} is ${v1}, ${v2} and ${v3}`)
} 

const Student = {
    name : 'Rahul',
    age : 25
}

const v1 = 'Brilliant'
const v2 = 'Attentive'
const v3 = 'Industrious'

const anotherFunc = printStudentName.bind(Student, v1, v2, v3)

anotherFunc()

/*
{ name: 'Rahul', age: 25 }
Rahul is Brilliant, Attentive and Industrious
*/

Here, the bind() function creates an instance of the function and stores it inside the anotherFunc. As a result, we need to call anotherFunc() to see the results.

New Binding

This keyword indicates a particular object that is created by a new keyword. Let’s imagine a constructor function that refers to a Students name and age. Now if we create an object with the new keyword then This keyword will refer to it. See the below code example:

function Student(name, age){
    this.name = name,
    this.age = age
    console.log(`${this.name} is ${this.age} years old.`)
}

const rahul = new Student('Rahul', 25)

// Output: Rahul is 25 years old.

Here, when we used the new keyword, JavaScript by default creates an Object named This and returns that Object. Now, when we used Rahul the This keyword simply identifies Rahul as This.

Window Binding

When the above three rules are not able to point to This keyword, then it will point to the window. In other words, This will refer to the Window Object by default. See the below code example:

const printName = function(){
    console.log(this.name)
    console.log(this)
}

const Rahul = {
    name : 'Rahul'
}

printName()

/* 
Output:
undefined
Window {window: Window, self: Window, document: document, name: '', location: Location, …}
<ref *1> Object [global] …}
*/

Here, you can see that This keyword does not follow the above three rules. As a result, we assume that it will bind to the window object. To check this, we print This. In the output, it shows the window for the browser and the global for the NodeJS. None of them contains a name property. So, when we try to print this.name it simply shows us undefined.

This behaviour of This keyword may often create confusion. Especially, when the code base becomes large. If we do not want this unwanted situation then we can simply use the use strict mode. See the below code example:

'use strict'

const printName = function(){
    console.log(this.name)
    console.log(this)
}

const Rahul = {
    name : 'Rahul'
}

printName()

// Output: TypeError: Cannot read properties of undefined (reading 'name')

Now, you can see that we are getting an error with a clear error message instead of getting undefined. This will help us to debug the code.

Arrow function

This keyword will behave completely differently when it comes to the Arrow function. The semantics of the Arrow function is different from the traditional functions of JavaScript. In the Arrow function, This will not be redefined. Instead, it will always represent the parent object. Let’s see the below code example without using the Arrow function.

const Student = {
    name : 'Rahul',
    printName: function () {
        console.log(`My name is ${this.name}`)
    },
}

Student.printName()

// Output: My name is Rahul

Here, you can see that we are getting the output with the name. The reason for this is that This keyword is indicating the Student object and the name is returned through the implicit binding. Now, what will happen if we use the Fat Arrow function instead of the traditional function? Let’s see the below code example:

const Student = {
    name : 'Rahul',
    printName:  () => {
        console.log(`My name is ${this.name}`)
    },
}

Student.printName()

// Output: My name is undefined

As expected, we are getting undefined instead of the name. The Arrow function does not have any execution context, as a result, it refers to the global Object or its Parent object. For this reason, the parent object print undefined.

Conclusion

I hope your found this tutorial helpful. The this keyword is pretty confusing and the only way to truly understand how it works is to try out the cases mentioned above and console.log() the value of this in the different cases. If you found this article helpful, you might find the article on Currying, Callback and Closure helpful as well.

Leave a Reply

Your email address will not be published. Required fields are marked *