Understanding JavaScript the weird parts: `this` context

Understanding JavaScript the weird parts: `this` context

The this keyword in JavaScript has confused a lot of developers. Whether you are just starting your career in programming or you are an experienced developer. It confuses everyone alike.


Now before starting let’s get into the fundamentals of how this works in javaScript. this always refers to the calling context of a function inside an object, which will usually be the object with which the function is associated with. Now, Since we have so many libraries at our disposal in the javascript ecosystem we just grab a library and start building something without actually understanding what is going on. While you will be able to build amazing applications but when it comes to debugging those applications that’s when the understanding of the weird parts of the javaScript comes into the picture. Now, javaScript is still evolving even after so many years but the fundamentals of language will always remain the same.


this does not refer to the function in which it is used, but always refers to the object in which it is executed. Let’s understand this by an example.

const obj={
  myFunction: function(){
  console.log(this===window)
 }
}

obj.myFunction()

Now, in the above example, we expect this behavior because here this will always refer to the calling context of a function which here is obj.

Now this behavior will be true in any other object-oriented language. This is the default assumption because this is how this works in most other languages. Now, let’s change a few things and see how the behavior of this changes.

Now, in this example object declaration is the same but here we assign it another variable and the call it afterward instead of calling it right away. Now if we call the newVariable, suddenly the value of this changes from obj to the global or window. Now, this tends to trip up a lot of developers. Now in order to understand what value this will hold we need to look where it is being called not where it is written. In the above example, it is being called in the global object and not the obj object.


Let's look at some complex examples.

const obj={
  myFunction: function(){
  console.log(this===obj)
  setTimeout(function(){
    console.log(this===obj)
    console.log(this===window)
  })
 }
}

obj.myFunction()

Now, this example is similar to the above example but here we use setTimeout which is an asynchronous task. Now, if we run this we get something different.

We see that inside setTimeout now the value of this again changes back to the window or global depending upon the environment i.e Nodejs or browser. Now even though it’s the same block of code the value of this changes to window. Now, going back to the first rule this does not depend on where the function is being written but where it is called and in case of asynchronous calls a new async function object on the window object. Okay, now let’s take a look at the same example but written a little differently using an ES6 arrow function.

const obj={
  myFunction: function(){
  console.log(this===obj)
  setTimeout(()=>{
    console.log(this===obj)
    console.log(this===window)
  })
 }
}

obj.myFunction()

Interestingly, Now the value of this changes back to obj instead of window. An important thing to note is that this always get binding happens in 3 ways- default binding, implicit binding, and explicit binding. Now whenever we define a standalone function execution, it is always a default binding and it always binds to window object.

Now, we have to keep that default binding will always be our fallback binding.


Let’s get to know a little bit about Explicit and Implicit binding and understand how that works.

Implicit Binding

Now implicit binding happens whenever we have a function call and whatever is to the left side of the dot it is going to refer to that.

In this example, we have obj to the left side of the dot so it is going to refer to that i.e obj.

<br>

Explicit Binding

Explicit binding of this occurs when .call(),.apply(), or .bind() are used on a function.

We call these explicit because you are explicitly passing in a this context to call() or apply(). Let’s take a look at how explicit binding looks like in the following example.

const obj={
  myFunction: function(){
  console.log(this===obj)
 }
}

const newFunctionVariable=obj.myFunction

newFunctionVariable.apply(obj)

Now even though we are assigning myFunction to a new variable we can still say to what this context this function call will be bound to. We can see this by looking at another example where we can bind it to a completely different object below.

const obj1={

firstName:"Sachin",

lastName:"Thakur",

myName:function(){

console.log(this.firstName+" "+this.lastName)

}

}

const obj={

myFunction: function(){

console.log(this)

console.log(this==obj1)

}

}

const newFunctionVariable=obj.myFunction

newFunctionVariable.apply(obj1)

Now, in this, if we pass the first parameter as obj1it will take the this reference of obj1 even though the function is defined on obj. And this is how the Explicit binding works.


Now with the introduction of ES5 arrow function, the javaScript engine introduced a new behavior. Before arrow functions, every new function defined its own this value based on how the function was called:

  • A new object in the case of a direct function call with window context as this (Default Binding)
  • undefined in strict mode function calls.
  • The base object if the function was called as an “object method”.(Implicit Binding)
  • You could also Explicitly define what this will refer to like we saw in the last example. (Explicit Binding)

An arrow function does not have it’s own this. The this value comes from the lexical scope. Arrow function follows the normal variable look rule. If the value is not found in its scope go one level up and find the value in the enclosing scope. That’s why we don’t need to bind this value to the object explicitly as long as it is available in it’s enclosing scope.

Thus, in the following code, the this within the function that is passed to setTimeout has the same value as the this in the lexically enclosing function:

<br>

const obj={
  myFunction: function(){
  console.log(this===obj)
  setTimeout(()=>{
    console.log(this===obj)
  },0)
 }
}
obj.myFunction()

Conclusion

this can be a little tricky sometimes but if we know the basic fundamentals of how scoping words and how javaScript treats an object, we can easily understand how these core concepts work. this can be a little tricky in case of callback or async function where the value of this changes. Always remember this value is assigned the value of the object where it is being invoked.