"this" in JavaScript

"this" in JavaScript

this can be that too...

We will try to understand how this works, with some concepts and examples as well.

this has different values depending on where it is used: So will understand this inside the following...

  1. Global Scope
  2. Objects
  3. Methods
  4. Functions (including strict mode)
  5. Inner Functions
  6. Constructor Functions
  7. Classes

Introduction to this

If you are inside a specific scope or object or function, this really means whichever object you are in.

There are some exceptions which we will look at

1. this inside Global Space

Variables declared Globally (outside any function) have Global Scope.

console.log(this); // window object
this.table = 'Window Table';
console.log(window.table); // Window Table

Here in the above example table is a property of window object, so when we print window.table it prints the value as Window Table.

Another example -

this.garage = {
    table: "Garage Table"
}

To invoke this above code, we can write it as

this.garage.table;
// Garage Table

window.garage.table;
// Garage Table

Here this is public property of window object because when you say this, you are creating a public property that can be accessible from outside.

2. this inside Object

this inside Object is a private variable or private scoped.

Example

let SoubhagyaRoom = {
    table: "Soubhagya Table"
}

Here to access My Table, we can't use this.SoubhagyaRoom.table. It will give a TypeError saying Cannot read property 'table' of undefined. As it's not accessible.

As its a private variable we have to use it as SoubhagyaRoom.table;

3. this inside a method

Objects can have methods as well.

In an object method, this refers to the "owner" of the method.

Example

let SoubhagyaRoom = {
    table: "Soubhagya's Table",
    cleanTable() {
         console.log(`Cleaning ${this.table}`)
    }
}

SoubhagyaRoom.cleanTable(); // Cleaning Soubhagya's Table

4. this inside a Function

Example

this.table = 'Window Table';
const cleanTable = function() {
    console.log(`Cleaning ${this.table}`);
}

cleanTable();

In the above example it will access the global scope and print Cleaning Window Table

But there is an exception to this while using the strict mode. The same function in strict mode will result in TypeError: Cannot read property 'table' of undefined

Using 'use strict'

'use strict'
this.table = 'Window Table';
const cleanTable = function() {
    console.log(`Cleaning ${this.table}`);
}

cleanTable();

Fix for this in 'use strict'

To resolve this issue we have call() to the rescue

'use strict'
this.table = 'Window Table';
const cleanTable = function() {
    console.log(`Cleaning ${this.table}`);
}

cleanTable.call(this);

It says, execute the cleanTable function as if its a method of this and this being in this case - Global Object

call() is basically used to call somebody else's method on somebody else's object.

Example

this.table = 'Window Table';
const cleanTable = function(soap) {
    console.log(`Cleaning ${this.table} using ${soap}`);
}

this.garage = {
    table: "Garage Table"
}

let SoubhagyaRoom = {
    table: "Soubhagya's Table"
}

cleanTable.call(this, 'some soap');
// Cleaning Window Table using some soap
cleanTable.call(this.garage, 'some soap');
// Cleaning Garage Table using some soap
cleanTable.call(SoubhagyaRoom, 'some soap');
// Cleaning Soubhagya's Table using some soap

5. this inside an Inner Function

Using 'use strict'

Example

'use strict'
this.table = 'Window Table';
const cleanTable = function(soap) {
  const innerFunction = function(_soap) {
    console.log(`Cleaning ${this.table} using ${_soap}`);
  };
  innerFunction(soap);
};

this.garage = {
    table: "Garage Table"
}

let SoubhagyaRoom = {
    table: "Soubhagya's Table"
}

cleanTable.call(this, 'some soap');
cleanTable.call(this.garage, 'some soap');
cleanTable.call(SoubhagyaRoom, 'some soap');

If we execute the above function then we will get TypeError: Cannot read property 'table' of undefined

This is because inside a function using this is not very useful unless you are using call() or bind().

So to use this inside the innerFunction, we need to let it know it's a method of some sort or it would execute on that particular object.

To fix the issue, we have 4 solutions. Let's take a look,

Solution 1 - Define this outside of innerFunction

'use strict'
this.table = 'Window Table';
const cleanTable = function(soap) {
  let that = this;
  const innerFunction = function(_soap) {
    console.log(`Cleaning ${that.table} using ${_soap}`);
  };
  innerFunction(soap);
};

this.garage = {
    table: "Garage Table"
}

let SoubhagyaRoom = {
    table: "Soubhagya's Table"
}

cleanTable.call(this, 'some soap');
cleanTable.call(this.garage, 'some soap');
cleanTable.call(SoubhagyaRoom, 'some soap');

Solution 2 - Using call() with innerFunction

'use strict'
this.table = 'Window Table';
const cleanTable = function(soap) {
  const innerFunction = function(_soap) {
    console.log(`Cleaning ${this.table} using ${_soap}`);
  };
  innerFunction.call(this,soap);
};

this.garage = {
    table: "Garage Table"
}

let SoubhagyaRoom = {
    table: "Soubhagya's Table"
}

cleanTable.call(this, 'some soap');
cleanTable.call(this.garage, 'some soap');
cleanTable.call(SoubhagyaRoom, 'some soap');

Solution 3 - Using bind() with innerFunction

'use strict'
this.table = 'Window Table';
const cleanTable = function(soap) {
  const innerFunction = function(_soap) {
    console.log(`Cleaning ${this.table} using ${_soap}`);
  };
  innerFunction.bind(this)(soap);
};

this.garage = {
    table: "Garage Table"
}

let SoubhagyaRoom = {
    table: "Soubhagya's Table"
}

cleanTable.call(this, 'some soap');
cleanTable.call(this.garage, 'some soap');
cleanTable.call(SoubhagyaRoom, 'some soap');

Solution 4 - Using arrow function

'use strict'
this.table = 'Window Table';
const cleanTable = function(soap) {
  const innerFunction = (_soap) => {
    console.log(`Cleaning ${this.table} using ${_soap}`);
  };
  innerFunction(soap);
};

this.garage = {
    table: "Garage Table"
}

let SoubhagyaRoom = {
    table: "Soubhagya's Table"
}

cleanTable.call(this, 'some soap');
cleanTable.call(this.garage, 'some soap');
cleanTable.call(SoubhagyaRoom, 'some soap');

Arrow functions are special because if you want to use this inside a function, it doesn't really know what to do with it.

But if I have an arrow function and if you use this inside, it will basically take this from the outer scope and use that.

6. this inside a Constructor

Example

let createRoom = function(name) {
  this.table = `${name}'s table`;
}

createRoom.prototype.cleanTable = function(soap) {
  console.log(this)
  console.log(`Cleaning ${this.table} using ${soap}`);
}

const smRoom = new createRoom('Soubhagya');
smRoom.cleanTable('some soap');

const rmRoom = new createRoom('Raghav');
rmRoom.cleanTable('some soap');

Instead of using the constructor function here, we can use classes.

7. this inside a class

Example

class createRoom {
  constructor(name) {
    this.table = `${name}'s table`
  }

  cleanTable(soap) {
    console.log(`Cleaning ${this.table} using ${soap}`)
  }
}

const smRoom = new createRoom('Soubhagya');
smRoom.cleanTable('some soap');

const rmRoom = new createRoom('Raghav');
rmRoom.cleanTable('some soap');

When we have a class, it pretty much does the same thing that a constructor function does and it has a prototype method called cleanTable.