22
this keyword in JavaScript
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.
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.
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"
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 .
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()
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
.
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".
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
method is used to call a function with the this
pointing to a thing of your choice.
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)
}
}
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.
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 is different from call and apply in the sense that bind returns a new function instead of calling the existing function immediately.
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 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"
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