DEV Community

Cover image for this keyword in JavaScript
Chinmay Mhatre
Chinmay Mhatre

Posted on

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)
Enter fullscreen mode Exit fullscreen mode

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.

plane

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"
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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 is fine


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
Enter fullscreen mode Exit fullscreen mode

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 );
Enter fullscreen mode Exit fullscreen mode

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

callback = movie.print
Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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) 
    }
}

Enter fullscreen mode Exit fullscreen mode

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) 
    }
}
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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) 
    }
}
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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"

Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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"

Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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()
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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()

Enter fullscreen mode Exit fullscreen mode

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()

Enter fullscreen mode Exit fullscreen mode

or bind it while passing it as a callback.

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

//output : jack is watching inception
Enter fullscreen mode Exit fullscreen mode
const callback = jack.display.bind(jack)
callback()
//output : jack is watching inception
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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.


that's all folk

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 :)

Discussion (11)

Collapse
lukeshiru profile image
Info Comment hidden by post author - thread only accessible via permalink
LUKESHIRU

Nice article! There are some things worth mentioning in every post related to this in JavaScript. Before I go into them, a TL;DR is:

just don't use this, you don't need it at all. It's unreliable, confusing and unnecessary.

If you want the details:

  • In Global Context: You should avoid any kind of global state.
  • Within a function: You should also avoid it here. You can use classes if you really want to use this in a function, or you can simply not use this at all, and use arrow functions instead.
  • Within an object: You can use closures instead. So ...
// Instead of doing this:
const movie = {
    name: "Peanut Butter Falcon",
    print: function () {
        console.log(this.name);
    }
};

// You can do this:
const movie = (() => {
    const name = "Peanut Butter Falcon";

    return {
        name,
        print: () => console.log(name)
    };
})();
Enter fullscreen mode Exit fullscreen mode
  • The weird part: You don't have any weirdness if you avoid this altogether. In the example above, nobody can change the value of name from the outside world, and if you assing print to another constant, it still prints the expected value.
  • Within class methods: You can live without classes, just using functions, arrays, objects and primitives. But yes, the only place where this makes some sense to be used, is within classes.
  • call/apply/bind: You don't have to use any of those unless you're working with classes.
  • Arrow Functions: One of the best additions to JavaScript, and one that you should use more often. The best thing about them is that they don't define their own this.
  • bind in ReactJS: React can be used without classes, and ideally you should. Nowadays React components are made using only functions and hooks. Again, no need for this or bind here :D

Cheers!

Collapse
chinmaymhatre profile image
Chinmay Mhatre Author • Edited on

Thank you for the feedback! I will update some the things like ideal usage. However, I do not believe " just don't use this" is point of the blog . In high school, we were taught about the atomic model . In that chapter we started with the " John Dalton " model , after teaching us the model the professor told us " You know what we don't use this model anymore " and the professor went on to teach us the rest of the 4 models. Thus , I believe even though there are better ways to do something we do need to know how things work in general.

Of course there won't be any weirdness if you avoid it .πŸ˜‚
My job while writing this blog was to give out as many cases and outcome as I could .
I do believe I should give out cases where it should actually be used.

In ReactJS, again yes there are function components! But what if someone's job or project just uses class components, I don't think being subjective while writing an article is the way to go , saying " you know what you can use function component just use those instead of class components " isn't a way I would like to go about things.πŸ˜‚

I don't think if anyone googled " this keyword in JavaScript " they want to see " just don't use it " πŸ˜‚.
Hence, "They won't do it" , "this won't happen" is subjectivity that I try to avoid.

Cheers!

Collapse
lukeshiru profile image
LUKESHIRU

My point mainly is that this kind of articles are great to deal with legacy, but not to create something from scratch.

You can go down the rabbit hole of trying to make this work as expected on your code, or you can just avoid it and be a happier dev.

I talked several times with folks having problems with this and the solution always was to just avoid it (classic XY problem). The thing is that generally is good to mention the solutions but also make clear that you should avoid it on your own code, and use it only if you're dealing with legacy that is hard to refactor.

In React the vast majority of projects using classes are on a migration path, or have tech-debt tickets related to turning old class components into functional ones, mainly because is kinda straightforward and has a better structure (no more this.method = this.method.bind(this) and all that jazz). One thing that is quite useful is to configure ESLint so it has rules to avoid using this in general. They can start as warnings and after a while turn them into linting errors :D

Thread Thread
chinmaymhatre profile image
Chinmay Mhatre Author • Edited on

My point still remains, the title of the blog is " this keyword in JavaScript " and not " why you shouldn't use this " or " why you should use functional components " the blog is meant to explain this as it is.

I think it always solidifies why we use arrow functions or functional components because it had me puzzled. Why use functional components? why does this cause so much confusion? I think those who seek those answers will find the blog useful . The way I write blog is by stumbling into things that I find confusing and I write about it thinking someone else would have the same doubt.

I think those who want to geek out on why things are the way they are would like to read the article. Those who want quick fixes won't and that's okay :)

Collapse
ashwith25 profile image
Ashwith Poojary

What an amazing article! Truly loved the way it is explained. Keep posting such informative blogs πŸ‘

Collapse
belidenikhil profile image
NikhilBelide

Thanks for this Chinmay πŸ”₯πŸ”₯πŸ”₯

Collapse
chinmaymhatre profile image
Chinmay Mhatre Author

πŸ‘€πŸ™ŒπŸ»

Collapse
ritikaradhakrishnan profile image
Ritika Radhakrishnan

this was truly helpful, on-point explanation made it easier to comprehend, keep posting more !

Collapse
prasad2200 profile image
prasad2200

Truly amazing article! Thank you for sharing such a good blog.

Collapse
samidha08 profile image
Samidha08

Very well drafted article, explains all the details in a very understandable manner! Good job.

Collapse
retumishra22 profile image
Retumishra22

Amazing article. One article to understand everything about 'this'.πŸ‘πŸ‘

Some comments have been hidden by the post's author - find out more