this keyword in JavaScript

Introduction

Hey everyone !
I have been recently learning ReactJS. While learning about class based components I stumbled across the bind keyword.

this.somefunction = this.somefunction.bind(this)

While trying to understand this line of code, I realized that the binding of the methods is not a React specific thing, It is a JavaScript thing .

I also realized that to understand the trio of call, apply and bind which basically do similar things, we need to understand the this keyword in JavaScript.

So, today I'd like to discuss about :

  • What is this ?
  • this in global scope
  • this within an object
  • this within a function
  • this within classes
  • The call, apply and bind methods

By learning about all these things we get to know why we bind methods in ReactJS.

Alright! let's get going.

So what is this all about ?

When we use 'this' in our daily conversations it always has a context attached with it, right? For instance, the sentence "This is my house" can only make sense in two ways when you are inside the house or you point to the house you are talking about.

If in the middle of the street you say "This is my house" it doesn't really make sense, because 'this' isn't really pointing to anything. If you have a picture of your house then regardless of the location you can point to the picture and attach the context to 'this', it will be like 'this house(in the picture) is my house'.

Similarly in JavaScript, this keyword will point to something according to the context it is in or we can point it to the thing we want using call, apply and bind . This will make better sense when we see the different contexts this can be defined in.

In Global Context

When we create a JavaScript file, even if there are zero lines of code written in the file a few things are created. One of the things created is the window object. The window object contains different properties and methods including things related to DOM.

When we write the this keyword in a global context ( outside any kind of function ), by default it points to the window object.

If we create a variable like this

this.thing = "window thing"

we could access it using this.thing as well as window.thing since this points to the window object.

console.log(this.thing)
    // # "window thing"
    console.log(window.thing)
    // # "window thing"

Strict Mode

Before we proceed to the context of a function, we need to understand strict mode.

In simple words, strict mode apply more restrictions and rules to JavaScript. Certain things would be allowed in non-strict mode but throw an error in strict mode.

We can enable strict mode using the statement "use strict" in the JS file .

Within a function

From here onwards, things will get a little weird, so stick with me.

function printsomething(){
    console.log(this) // Window object
}
printsomething()

When we call this in a function in non-strict mode it points to the window or the global object. In strict mode this is undefined and it looses the context.

"use strict"
function printsomething(){
    console.log(this) // undefined
}
printsomething()

Within an object

When we call the this keyword in an object method, this points to the object it was defined in.

For Example :

this.name = "Some other movie"
const movie = {
    name : "Peanut Butter Falcon",
    print : function (){
        console.log(this.name) 
    }
}

movie.print()

// OUTPUT
// Peanut Butter Falcon

In the above example, we define a method called print within the object movie. The object movie has a property called name.

In print we call console.log(this.name) which would basically point to the name property of the movie object.

It will not print "Some other movie" since the print function is in the context of the object movie.

The weird part

Remember when I told you that the value of this depends on the way it was called?
Let us use the same object we used previously to understand this.

What if we assign the method print to another variable called globalprint?

this.name = "Rocky"
const movie = {
    name : "Peanut Butter Falcon",
    print : function (){
        console.log(this.name) 
    }
}
const globalprint = movie.print

globalprint()
//output : Rocky

Now, the context of this has changed to the global context since globalprint is not a method of the movie object, it is an independent function as we have seen in the function section, points to the global context in non-strict mode and is undefined in strict mode.

So, the output would be "Rocky" instead of "Peanut Butter Falcon".

This also applies to callback functions.

this.name = "Rocky"

const movie = {
    name : "Peanut Butter Falcon",
    print : function (){
        console.log(this.name) 
    }
}

setTimeout(movie.print , 1000 ); // Output : Rocky

We can try to fake a setTimeout method to look at how this works.

function setTimeout(callback, delay){

   //wait for 'delay' milliseconds
   callback();

}

setTimeout( movie.print, 1000 );

Internally the setTimeout method assigns the movie.print to it's callback argument.

callback = movie.print

As we saw before, assigning a method to another variable changes it's context. Thus print will be "undefined" in strict mode and in non-strict mode
it will print "Rocky".

Within class methods

class theatre {
    constructor(person,movie){
        this.person = person
        this.movie = movie
    }
    display(){
        console.log(`${this.person} is watching ${this.movie}`)
    }
}

const peter = new theatre("peter","interstellar")
const jack = new theatre("jack","inception")

jack.display()
//output : peter is watching interstellar
peter.display()
//output : jack is watching inception

Within a class this point to the current instance of the class.
Here the instances being jack and peter.

However, the class methods like display will lose context if passed as a callback function or assigned to another variable like we saw in the functions section.

Class expression and methods such as the constructor or any class methods are always executed in strict mode.

Thus callbacks, instead of taking the global context, will be undefined.

const callback = jack.display
callback() // "this" will be undefined

We will be learning about bind method shortly to fix this problem. To give you an idea , bind will basically glue value of this to the display function and it will always be called in that context, fixing the problem of the context getting lost.

Remember the house analogy I used in the beginning?
We'll now understand the pointing at the picture of the house part, that is basically telling javascript explicitly where you want this keyword to point rather than JavaScript assigning it for you.

call

call method is used to call a function with the this pointing to a thing of your choice.

Syntax

call(thisArg, arg1, ... , argN)

thisArg helps the call function to knw on which this should the function be called.
Rest arguments are the arguments passed to the function.

Let's understand the use case with an example.

const movie = {
    name : "Peanut Butter Falcon",
    print : function (){
        console.log(this.name) 
    }
}

Remember this object?
What if we create two movie objects with different movies?

const movie1 = {
    name : "Peanut Butter Falcon",
    print : function (){
        console.log(this.name) 
    }
}
const movie2 = {
    name : "The imitation game",
    print : function (){
        console.log(this.name) 
    }
}

We can see here the print function is repeated. If there were more of these
objects, we would have to write the same print function multiple times.

We could use a call method in this case.

We would remove the print method out of the objects and make it as a separate function .

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

const movie1 = {
    name : "Peanut Butter Falcon"   
}
const movie2 = {
    name : "The imitation game"
}

Now, we will use the call method on the printThings function with reference to whichever this value we want

printThings.call(movie1) // output : "Peanut Butter Falcon"
printThings.call(movie2) // output : "The imitation game"

printThings.call(movie1) tells JavaScript that we want the above function's this to point to movie1 object and similarly for movie2.

It is as though the function is inside the object like given below.

const movie1 = {
    name : "Peanut Butter Falcon",
    print : function (){
        console.log(this.name) 
    }
}

What if there are parameters in the function, how do we pass those ?

const printThings = function (person){
        console.log(`${person} is watching ${this.name}`) 
    }

const movie1 = {
    name : "Peanut Butter Falcon"   
}
const movie2 = {
    name : "The imitation game"
}

The printThings function now has an parameter called person.

So how do we use the call method and pass the arguments?

printThings.call(movie1,"James") // output : "James is watching Peanut Butter Falcon"
printThings.call(movie2,"Peter") // output : "Peter is watching The imitation game"

The first argument is always the this argument , Rest can be passed after it like it is done in the above example.

Let's take one more example :

const printprintThings = function (fname, lname){
        console.log(`${fname} ${lname} is watching ${this.name}`) 
    }

const movie1 = {
    name : "Peanut Butter Falcon"   
}
const movie2 = {
    name : "The imitation game"
}

printThings.call(movie1,"James","Bond") // output : "James Bond is watching Peanut Butter Falcon"
printThings.call(movie2,"Peter", "Pan") // output : "Peter Pan is watching The imitation game"

This example just has one more parameter than the previous one.

apply

The only difference between call and apply is that apply method calls a function with a this value and arguments as an array instead of passing the arguments individually like the call method.

apply(thisArg,argArray)

We could use the previous example and use apply on it instead of call.

const printThings = function (fname, lname){
        console.log(`${fname} ${lname} is watching ${this.name}`) 
    }

const movie1 = {
    name : "Peanut Butter Falcon"   
}
const movie2 = {
    name : "The imitation game"
}

printThings.apply(movie1,["James","Bond"]) // output : "James Bond is watching Peanut Butter Falcon"
printThings.apply(movie2,["Peter", "Pan"]) // output : "Peter Pan is watching The imitation game"

It gives the same result.

bind

Bind is different from call and apply in the sense that bind returns a new function instead of calling the existing function immediately.

Syntax

bind(thisArg, arg1, ... , argN)

Now, we could bind the function to a this value .

let printThings = function (fname, lname){
        console.log(`${fname} ${lname} is watching ${this.name}`) 
    }
    printThings = printThings.bind(movie1,"James","Bond")

printThings()
// output : "James Bond is watching Peanut Butter Falcon"

Thus we bind the printThings function to movie1 and we can call it whenever we want.

Let us take one more example.

"use strict"
    this.movie = "Saving Private Ryan"
    const outerFunction = function(){
        const innerFunction = function (){
            console.log(this.movie)
        }
        innerFunction()
    }
    outerFunction()

From what we have seen, the above code won't work, right? Since the context is lost.

Let us bind the outerFunction and use call on the inner function and give them the this value

"use strict"
    this.movie = "Saving Private Ryan"
    let outerFunction = function(){
        const innerFunction = function (){
            console.log(this.movie)
        }
        innerFunction.call(this) 
        //Here "this" means the outerFunction
    }
    outerFunction = outerFunction.bind(this) 
    // Here "this" means the global context
    outerFunction()

    //Output : "Saving Private Ryan"

Finally, Let's try to fix the class which we made in the Within a class section. click here to take a look at it again

class theatre {
    constructor(person,movie){
        this.person = person
        this.movie = movie

    }
    display(){
        console.log(`${this.person} is watching ${this.movie}`)
    }
}

const jack = new theatre("jack","inception")

const callback = jack.display
callback()

The only thing we have to do to get this working is to bind the method display to the this value of the class.

We can do this either in the constructor

class theatre {
        constructor(person,movie){
            this.person = person
            this.movie = movie
            this.display = this.display.bind(this) // Here
        }
        display(){
            console.log(`${this.person} is watching ${this.movie}`)
        }
    }

    const jack = new theatre("jack","inception")

    const callback = jack.display
    callback()

or bind it while passing it as a callback.

setTimeout(jack.display.bind(jack),1000)

//output : jack is watching inception
const callback = jack.display.bind(jack)
callback()
//output : jack is watching inception

Arrow Functions

Arrow functions are another way to solve the binding problem.

Arrow function have something called as a "lexical scope".

What that means is that arrow function does not have it's own this context, it borrows the context from the parent element or the context it was defined in.

Hence, in the above example the outer function will get it's context from the global context and the inner function will get it's context from the outer function .

"use strict"
    this.movie = "Saving Private Ryan"
    const outerFunction = () => {
        const innerFunction = () => {
            console.log(this.movie)
        }
        innerFunction()
    }
    outerFunction()
    //Output : "Saving Private Ryan"

bind in ReactJS

We have already seen all the javascript specific reasons we bind a function or a method. The same applies for ReactJS.

In React we generally call a class method using an event handler like onClick, we pass the method we want to execute as a callback to the handler function.

That is why the value of this is lost .

By binding it explicitly we make sure it doesn't happen.

I hope you learnt something from this blog.

Let me know in the comments if you found this helpful .

See you in the next one :)

22