Header Ads Widget

THIS keyword, Misconception about this keyword, what is execution context in JavaScript?

THIS keyword in JavaScript is the most powerful mechanism but also the most misunderstood one too.

The common confussions about "this" keyword are-

this of a function, refers to the function itself. But it is not true. Unlike other programming languages, "this" does not refer to the function itself.

Another misconception is that- this points to the instance that a method belongs to. Again this is not true.

This keyword and execution context in javascript

What is this keyword then?

If you are not very much familiar with the object-oriented paradigm of Javascript then you must be wondering - What is the this keyword in JavaScript? How does this keyword work in JavaScript? What is the use of this?

I will explain this keyword in simple words-

"This" keyword is an object which is not tightly coupled with a function. A function uses the value of "this" from its environment where it is executed. this keyword is part of current context of the function(during execution) which can be changed by binding the new context.

Now you must be wondering, what is execution context ?
Execution context is just an abstract concept. It is the environment in which the JavaScript code is executed. It holds information like the value of "this", variables, objects, and functions.

Javascript engine decides the value of this at execution time. Execution time means- Actual time when your code run. May be while writing your code it points to this but while execution this is changed to that. It is because, all the bindings, references, and memory assignments are done at execution time.

Let's understand this keyword step by step.

In the global context, "this" refers to the window object. By default within a function, this is bound to window object. Check the below example.

  cosole.log(this) //window object
  function test(){
      cosole.log(this);
  }
  test() //window object
  

Check the below example 👇, this.title print the global variable title. It is because, title is global variable and thus becomes part of the window object. And this within function points to window object.

  var title = 'ui dev';
  function printTitle() {
    console.log(this.title);
  }
  printTitle(); //ui dev
  

Remember: When the JavaScript engine starts executing the code, very first thing it does is creates a global execution context. The global execution context is initialised with the window object. At that point of time, "this" points to the window object.

  console.log(window === this) //true
  function test(){
      console.log(window === this);
  }
  test(); //true
  

Within a function, by default, this refers to the Global object. But, If we use strict mode, this is undefined within the function. It is because, strict mode does not allow default binding.

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

In an object method, this refers to the "owner" of the method that is- object itself.

  var student = {
    name: "John",
    age    : 30,
    printDetail: function(){
    	console.log(this.name, ' ', this.age)
    }
  };
  student.printDetail()// John 30
  

printDetail is a method within the Student Object, this used within that method refers to the "owner" of the method, that is Student object.

In the below example, printThis is a function that simply prints this value that is window.

  function printThis(){
    console.log(this); //window object
  }
  var person = {
    firstName: "John",
    lastName : "Doe",
    print    : printThis, 
  };
  
  person.print() 
  //{firstName: "John", lastName: "Doe",  print: Æ’}
  
  
  var printRef = person.print;
  printRef() // window object
  

If we directly call printThis() function it will print the global window object.

But if we assign it to print of person object and execute the print method directly, person.print() it will log the person object itself.

However, if we hold the reference of person.print to another variable and execute it later, it will log the window object. This is because the execution context is changed and now this points to window object. We are not doing anything but the JS engine is doing implicit bindnig work depends on the current execution context.

In the above example, person.print is giving 2 different output because of the change of the execution context. When we directly invoke person.print() method, it runs within the context of the person object.
Remember, this keyword of a method within an Object refers to the "owner" of the method that is the object itself. In the case of the above example, it is the person object.

But, When we hold the reference of person.print to another variable that is printRef, it means that print (refer to printThis function) is out of the person execution context. Now, the value of printRef() will depend on where are we executing it.

If we execute it within the global context, the value will be the global window object (as demonstrated in the above example). It is also called implicit binding.

But, If we change the execution context explicitly then the value of this will also change. The below example shows explicit binding

  function printThis(){
    console.log(this); //window object
  }
  var person = {
    firstName: "John",
    lastName : "Doe",
    print    : printThis, 
  };
  
  
  var newObj = {
    fname: "New John",
    lastName : "New Doe",
  }
  
  var printRef = person.print;
  
  printRef.call(newObj);
  //{fname: "New John", lastName: "New Doe"}
  
  OR
  
  var printRef = person.print.bind(newObj);
  printRef()
  //{fname: "New John", lastName: "New Doe"}
  

Now, let's assign printThis method to the member of the Student constructor.

  function printThis(){
    console.log(this); //window object
  }
  function Student(){
	this.name = "John";
	this.print = printThis;
  };
  var obj = new Student();
  obj.print(); // {name: "John", print: Æ’}
  

In the above example, obj.print() is nothing but the printThis() function itself which is executing within the context of Student. obj is an object (instance) created from the Student constructor. If we invoke obj.print(), it will print the instance of student that is - Studnet object.

Within a constructor context, this refers to the instance of that constructor.

  function Student(){
    this.name = "John";
    this.print = function(){
        console.log(this);
    }
  };
  var obj = new Student();
  obj.print(); // {name: "John", print: Æ’}
  
  obj instanceof Student; //true
  

We can use methods like- call(), bind() and apply() to change the this of any function. These special methods execute the function in the provided object context.

  var name = "name from window";
  function Print(){
    console.log(this.name);
  };
  Print(); //name from window

  var obj = {
      name: "name from obj",
   }

  Print.call(obj);
  

call(), bind() and apply() behaves differently than the new Keyword. When we use new keyword, it invoke the function as constructor and returns an object which contains whatever is attached with this within that function. If nothing is attached to this keyword then it returns an empty object.

  function Print(){
    console.log(this.name);
  };
  var obj = new Print();
  console.log(obj) // {}
  

However, when we use call(), bind() and apply(), it execute the function with new context instead returing a object like new keyword. call() and apply() execute the function immediately but, bind() just bind the new context with the function and return the reference of that function which we can execute later.

A function's this loses its context when executed within setTimeout.

  var name = "Global John";
  var obj = {
    name: "John", 
    print: function() {
        console.log(this.name);
    },
  }
  obj.print(); //John
  setTimeout(obj.print, 2000); //Global John
  

As you can see, obj.print is executing withing global context not the obj context.

We can explicitly bind the "this" with the function'. In this example, it is obj which we can bind with the function.

  setTimeout(obj.print.bind(obj), 2000); //John
  

Let's see some more examples:-

  var name = "Global John";
  var obj = {
    name: "John", 
    print: function() {
        setTimeout(() => {
          console.log(this.name);
        }, 2000);
    },
  }
  obj.print();
  

Can you guess, what will be the output of the above code? Take some time and try to figure it out. Think in terms of the execution context.

  output: John
  

Let's tweak the code. Let's use the arrow function within setTimeout.

  var name = "Global John";
  var obj = {
    name: "John", 
    print: ()=> setTimeout(() => console.log(this.name), 2000),
  }
  obj.print(); //Global John
  

Just by changing the function to arrow function this points to global context. This is because the arrow function does not have its this contest, instead it take it from from parent lexical scope.

I hope this article was helpful to you. Now you must be a little more confident than earlier.

JavaScript this keyword

Post a Comment

0 Comments