JavaScript: this – [full explanation]

If you are working with JavaScript for a while, you will agree with me. This ‘this‘ keyword is very confusing when you are writing code, especially when you read others’ code.

This article’s goal is to eradicate those confusions.

You have to remember a few things about ‘this‘ and consider where you are using it and how you are using it.

Let’s check the behavior of “this” in different contexts-

Case #1: In the global execution context

Type the following line in your browser console.

// 'this' in global context
console.log(this); // here 'this' refers to global object, if browser it is 'window' and in NodeJS it is 'global'

So in the global context, ‘this‘ refers to the global object.

In the browser “this” refers to the ‘window‘ object-

Window {0: Window, window: Window, self: Window, document: document, name: '', location: Location, …}

In NodeJS, it refers to the ‘global‘ object-

<ref *1> Object [global] {
  global: [Circular *1],
  clearImmediate: [Function: clearImmediate],
  setImmediate: [Function: setImmediate] {
    [Symbol(nodejs.util.promisify.custom)]: [Getter]
  },
  clearInterval: [Function: clearInterval],
  clearTimeout: [Function: clearTimeout],
  setInterval: [Function: setInterval],
  setTimeout: [Function: setTimeout] {
    [Symbol(nodejs.util.promisify.custom)]: [Getter]
  },
  queueMicrotask: [Function: queueMicrotask],
  structuredClone: [Getter/Setter],
  atob: [Getter/Setter],
  btoa: [Getter/Setter],
  performance: [Getter/Setter],
  fetch: [AsyncFunction: fetch],
  crypto: [Getter]
}

For example, let’s type the following code in the browser console(or put it in some JS file and execute it in the browser)-

this.bigBoxVal = 100;

console.log("window.bigBoxVal = " + window.bigBoxVal); // logs 100
console.log("this.bigBoxVal = " + this.bigBoxVal); // logs 100
console.log("bigBoxVal = " + bigBoxVal); // logs 100

The output will be as below-

window.bigBoxVal = 100
this.bigBoxVal = 100
bigBoxVal = 100

So the “window” object, and “this” refer to the same object when used in the global context.

Case #2: In a function which is in the global execution context

In a function in a global context, ‘this‘ refers to the global object; in a browser, it refers to the ‘window‘.

function testFunc() {
  console.log(this);
}

testFunc(); // it will log 'window' object

But when you use strict mode by ‘use strict‘, in the same use in a function, it will refer to ‘undefined

'use strict'

function testFunc() {
  console.log(this);
}

testFunc(); // it will log 'undefined'

Case #3: In a function when it’s initiated with “new” binding

If a new instance of a function is created with the ‘new‘ keyword, then ‘this‘ inside the function will refer to the object’s instance.

Let’s check how “this” behaves if a function initates with new binding-

function Address(country, state, city, zip) {
  this.country = country;
  this.state = state;
  this.city = city;
  this.zip = zip;

  this.printAddress = function() {
   console.log(this);
   console.log('Country: ' + this.country + ', State: ' + this.state + ', City: ' + this.city + ', Zip: ' + this.zip);
  }
}

let myAddress = new Address('myCountry', 'myState', 'myCity', 1000);

myAddress.printAddress();
// for console.log(this), it will print
// Address {country: "myCountry", state: "myState", city: "myCity", zip: 1000, printAddress: ƒ}

// for address console.log it wll print
// Country: myCountry, State: myState, City: myCity, Zip: 1000

Case #4: In a function that belongs to an object

When used in a function in an object, ‘this‘ will refer to that object. This is called implicit binding.

let address = {
  country: 'myCountry',
  state: 'myState',
  city: 'myCity',
  zip: 1000,
    
  printAddress: function() {
    console.log(this); 
    console.log('Country: ' + this.country + ', State: ' + this.state + ', City: ' + this.city + ', Zip: ' + this.zip);
  },
};

address.printAddress();

// here console.log(this) print the 'address' object
// {country: "myCountry", state: "myState", city: "myCity", zip: 1000, printAddress: ƒ}

// and the second console.log prints
// Country: myCountry, State: myState, City: myCity, Zip: 1000

Output:

{country: 'myCountry', state: 'myState', city: 'myCity', zip: 1000, printAddress: ƒ}city: "myCity"country: "myCountry"printAddress: ƒ ()state: "myState"zip: 1000[[Prototype]]: Object

Country: myCountry, State: myState, City: myCity, Zip: 1000

Case #5: In function of object, assigned to a variable

If we assign the printAddress function to a variable and then call that, then the “this” inside printAddress refers to the global object-

let address = {
  country: 'myCountry',
  state: 'myState',
  city: 'myCity',
  zip: 1000,
    
  printAddress: function() {
    console.log(this); 
    console.log('Country: ' + this.country + ', State: ' + this.state + ', City: ' + this.city + ', Zip: ' + this.zip);
  },
};

// Assign the printAddress to a constant
const addressFun = address.printAddress;

// Call the function
addressFun();

// Logs Window object as log of this
// Window {0: Window, window: Window, self: Window, document: document, name: '', location: Location, …}

// The values are logged as undefined, as window object does not have the values
// Country: undefined, State: undefined, City: undefined, Zip: undefined

This will output-

Window {0: Window, window: Window, self: Window, document: document, name: '', location: Location, …}

Country: undefined, State: undefined, City: undefined, Zip: undefined

Case #6: In function of object, define a function inside

Here we have a function named “printAddress” in the object “address“.

Inside the “printAddress” function we have defined a new function named “bigBoxInsider“. In the function “bigBoxInsider” if we log “this” then it refers to the global “window” object.

let address = {
  country: 'myCountry',
  state: 'myState',
  city: 'myCity',
  zip: 1000,
    
  printAddress: function() {
    console.log(this); 
    console.log('Country: ' + this.country + ', State: ' + this.state + ', City: ' + this.city + ', Zip: ' + this.zip);

    // If we try to assign some value here to this.country, then that will work and change the value of country property of current object

      // Declare a function here inside a function
      function bigBoxInsider() {
          console.log("Inside bigBoxInsider: ", this);

          // Here it logs
          // Inside bigBoxInsider:  Window {0: Window, window: Window, self: Window, document: document, name: '', location: Location, …} 
      }

      // Call the function here
      bigBoxInsider();
  },
};

address.printAddress();

// here console.log(this) print the 'address' object
// {country: "myCountry", state: "myState", city: "myCity", zip: 1000, printAddress: ƒ}

// and the second console.log prints
// Country: myCountry, State: myState, City: myCity, Zip: 1000

Output-

{country: 'myCountry', state: 'myState', city: 'myCity', zip: 1000, printAddress: ƒ}

Country: myCountry, State: myState, City: myCity, Zip: 1000

Inside bigBoxInsider:  Window {0: Window, window: Window, self: Window, document: document, name: '', location: Location, …}

If we want to use the parent object inside the “bigBoxInsider” function then we can assign “this” to a variable or constant inside the “printAddress” function.

Then we can use that variable/constant in the “bigBoxInsider” to get the address object. Check the code below-

let address = {
  country: 'myCountry',
  state: 'myState',
  city: 'myCity',
  zip: 1000,
    
  printAddress: function() {
    // Assign this to a local variable or constant for later usages
    const currentObj = this;
      
    console.log(this);
    console.log('Country: ' + this.country + ', State: ' + this.state + ', City: ' + this.city + ', Zip: ' + this.zip);

      // Declare a function here inside a function
      function bigBoxInsider() {
          console.log("Inside bigBoxInsider: ", this);
          // Here it logs
          // Inside bigBoxInsider:  Window {0: Window, window: Window, self: Window, document: document, name: '', location: Location, …}

          console.log("currentObj: ", currentObj);
          // Here it logs
          // currentObj:  {country: 'myCountry', state: 'myState', city: 'myCity', zip: 1000, printAddress: ƒ}
      }

      // Call the function here
      bigBoxInsider();
  },
};

address.printAddress();

// here console.log(this) print the 'address' object
// {country: "myCountry", state: "myState", city: "myCity", zip: 1000, printAddress: ƒ}

// and the second console.log prints
// Country: myCountry, State: myState, City: myCity, Zip: 1000

Output-

{country: 'myCountry', state: 'myState', city: 'myCity', zip: 1000, printAddress: ƒ}

Country: myCountry, State: myState, City: myCity, Zip: 1000

Inside bigBoxInsider:  Window {0: Window, window: Window, self: Window, document: document, name: '', location: Location, …}

currentObj:  {country: 'myCountry', state: 'myState', city: 'myCity', zip: 1000, printAddress: ƒ}

Or, we can invoke the function call using “call()” and pass “this” to it. This way the context is passed to the function “bigBoxInsider” through the “this” object of “printAddress“. Check the code below-

let address = {
  country: 'myCountry',
  state: 'myState',
  city: 'myCity',
  zip: 1000,
    
  printAddress: function() {      
    console.log(this);
    console.log('Country: ' + this.country + ', State: ' + this.state + ', City: ' + this.city + ', Zip: ' + this.zip);

      // Declare a function here inside a function
      function bigBoxInsider() {
          console.log("Inside bigBoxInsider: ", this);
          // Here it logs
          // Inside bigBoxInsider:  {country: 'myCountry', state: 'myState', city: 'myCity', zip: 1000, printAddress: ƒ}
      }

      // Envoke the function by usign call() and pass "this" to it, to define the context
      bigBoxInsider.call(this);
  },
};

address.printAddress();

// here console.log(this) print the 'address' object
// {country: "myCountry", state: "myState", city: "myCity", zip: 1000, printAddress: ƒ}

// and the second console.log prints
// Country: myCountry, State: myState, City: myCity, Zip: 1000

Output will be-

{country: 'myCountry', state: 'myState', city: 'myCity', zip: 1000, printAddress: ƒ}

Country: myCountry, State: myState, City: myCity, Zip: 1000

Inside bigBoxInsider:  {country: 'myCountry', state: 'myState', city: 'myCity', zip: 1000, printAddress: ƒ}

The behavior is different if we define an arrow function inside. In the inside function “this” will refer to the object.

Check the code below-

let address = {
  country: 'myCountry',
  state: 'myState',
  city: 'myCity',
  zip: 1000,
    
  arrowFuncDemo: function() {
      console.log('Arrow function demo:');
      
      var fooBar = () => {
          console.log(this);
      }

      fooBar();
  },
};

address.arrowFuncDemo();

Output will be-

Arrow function demo:
{country: 'myCountry', state: 'myState', city: 'myCity', zip: 1000, arrowFuncDemo: ƒ}

Case #7: In function prototype methods like call(), apply() or bind()

It is possible to set a custom object for ‘this‘ using the function prototype methods. Like with call() or apply(), you can do something like this:

function Address(country, state, city, zip) {
  this.country = country;
  this.state = state;
  this.city = city;
  this.zip = zip;

  this.printAddress = function() {
   console.log(this);
   console.log('Country: ' + this.country + ', State: ' + this.state + ', City: ' + this.city + ', Zip: ' + this.zip);
  }
}

let myAddress = new Address('myCountry', 'myState', 'myCity', 1000);
let yourAddress = new Address('yourCountry', 'yourState', 'yourCity', 2000);

myAddress.printAddress.call(yourAddress);

// for the first console.log it will log
// Address {country: "yourCountry", state: "yourState", city: "yourCity", zip: 2000, printAddress: ƒ}

// for the second console.log it will print
// Country: yourCountry, State: yourState, City: yourCity, Zip: 2000

The same thing can be done with bind():

function Address(country, state, city, zip) {
  this.country = country;
  this.state = state;
  this.city = city;
  this.zip = zip;

  this.printAddress = function() {
   console.log(this);
   console.log('Country: ' + this.country + ', State: ' + this.state + ', City: ' + this.city + ', Zip: ' + this.zip);

  }
}

let myAddress = new Address('myCountry', 'myState', 'myCity', 1000);
let yourAddress = new Address('yourCountry', 'yourState', 'yourCity', 2000);

let addressPrint = myAddress.printAddress.bind(yourAddress);

addressPrint();
// for the first console.log it will log 
// Address {country: "yourCountry", state: "yourState", city: "yourCity", zip: 2000, printAddress: ƒ} 

// for the second console.log it will print 
// Country: yourCountry, State: yourState, City: yourCity, Zip: 2000

Leave a Comment


The reCAPTCHA verification period has expired. Please reload the page.