this Keyword in JavaScript

Ridam SinghalRidam Singhal
14 min read

The this keyword refers to the context where a piece of code is running.

OR

this keyword batata hai ki kaha pe (context) code run kar raha hai.

The value of this in JavaScript depends on where the function is invoked or called, not where it is defined. For example:

When a regular function is invoked as a method of object (obj.method()), this points to that object.

const obj = {
    name: "John",
    age: 30,
    method() {
        console.log(this);            // {name: 'John', age: 30, method: ƒ} (refering to object)
        return `Name is ${this.name} and age is ${age}`
    }
}

console.log(obj.method()).            // Name is John and age is 30

When invoked as a standalone function (not attached to any object: obj.functionName()), this typically refers to the global object (in non-strict mode) or undefined (in strict mode).

  • In non-strict mode:

      function tellMeWhatIsThis() {
          console.log(this)
      }
    
      tellMeWhatIsThis()      
      // Global Object in case of Browser which is window
      // Window {window: Window, self: Window, document: document, name: '', location: Location, …} 
    
      // In case of Node js
      // Node js Global Object 
      // To see Node js global object you can do "console.log(globalThis)"
    
  • In Strict mode:

      "use strict"
    
      function tellMeWhatIsThisInStrictMode() {
          console.log(this)
      }
    
      tellMeWhatIsThisInStrictMode()  
    
      // undefined (both browser and nodejs)
    

When we simply logs this in the global scope it refers to global object in browser, and empty parenthesis {} in the node js environment.

console.log(this)

// Global Object in case of Browser which is window
// Window {window: Window, self: Window, document: document, name: '', location: Location, …} 

// Nodejs
// {}

this keyword in Strict v/s Non-Strict Mode

this keyword behave differently in strict and non-strict mode.

In Non-Strict Mode

The value of this in non-strict mode is forced to be an object. If the value of this is already an object then this object becomes the value of this. If the value of this is null or undefined then the new value of this becomes global object and if the value of this is any primitive value, then this primitive value is wrapped (or boxed) to there corresponding wrapper object.

NOTE

In JavaScript, "boxing" refers to the process where a primitive value (like a number, string, or boolean) is wrapped inside an there wrapper object.

function whatWillBeThis() {
    console.log(this)
}

whatWillBeThis()                                    // Global Object (window in browser)
whatWillBeThis.call(null)                           // Global Object (window in browser)
whatWillBeThis.call(undefined)                      // Global Object (window in browser)
whatWillBeThis.call(42)                             // Number {42}
whatWillBeThis.call("String")                       // String {'String'}
whatWillBeThis.call(true)                           // Boolean {true}
whatWillBeThis.call(3n)                             // BigInt {3n}
whatWillBeThis.call(Symbol("symbol"))               // Symbol {Symbol(symbol), description: 'symbol'}

In Strict Mode

In strict mode, the value of this is not forced to be an object. If the value of this is undefined then it will be undefined, if it is null then it will be null and if it is any primitive value then these primitive values will also be not boxed into there wrapper object and the value of this will simply be that primitive value.

"use strict"

function whatWillBeThis() {
    console.log(this)
}

whatWillBeThis()                                    // undefined
whatWillBeThis.call(null)                           // null
whatWillBeThis.call(undefined)                      // undefined
whatWillBeThis.call(42)                             // 42
whatWillBeThis.call("String")                       // "String"
whatWillBeThis.call(true)                           // true
whatWillBeThis.call(3n)                             // 3n
whatWillBeThis.call(Symbol("symbol"))               // Symbol(symbol)

IMPORTANT POINT

In JavaScript when you simply call the function the value of this inside it is undefined but as I had already told you that if the value of this is undefined it will be forced to become global object so that’s way when we simply call whatWillBeThis() in strict mode it logs undefined and in non-strict mode it logs global object. This process where the value of this is getting replaced by objects in non-strict mode is also referred to as this substitution.

So, now if I tell you the difference between strict and non-strict mode in JavaScript, it simply is in non-strict mode this substitution takes place and in strict mode no this substitution takes place.

this keyword in Node v/s Browser

this keyword also behave different in Node js and in Browser. In Node js all modules (or script files) are executed in their own module scope which is an empty object {} while browsers execute all script files directly within the global scope so this is window. Read more about it here:

console.log(this)

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

// Nodejs
// {}

The below picture shows the value of this in browser and in nodejs:

  • Strict Mode

      "use strict"
    
      console.log("this outside function: ", this)
    
      function tellMeValueOfThis() {
          console.log("this inside function: ", this)
      }
    
      tellMeValueOfThis()
    
      // Browser
      // this outside function:  Window {window: Window, self: Window, document: document, name: '', location: Location, …}
      // this inside function:  undefined
    
      // Node js
      // this outside function:  {}
      // this inside function:  undefined
    
  • Non-Strict Mode

      console.log("this outside function: ", this)
    
      function tellMeValueOfThis() {
          console.log("this inside function: ", this)
      }
    
      tellMeValueOfThis()
    
      // Browser
      // this outside function:  Window {window: Window, self: Window, document: document, name: '', location: Location, …}
      // this inside function:  Window {window: Window, self: Window, document: document, name: '', location: Location, …}
    
      // Node js
      // this outside function:  {}
      // this inside function:  Node js Global Object
    

this keyword in context of Normal Function

Inside the function, the value of this depends on how the function is called.

For a regular function, the value of this is the object that had called the function. For example if there is an object that had a method defined in it, then the value of this inside that method will be the object that had called the function. For example:

If function call is in the form of obj.f(), then this inside f function refers to obj.

Consider this code:

let obj = {
    name: "Aditya",
    age: 44,
    getDetails() {
        console.log("The value of this: ", this)               // The value of this:  {name: 'Aditya', age: 44, getDetails: ƒ}
        return `My name is ${this.name} and age is ${this.age}`
    }
}

console.log(obj.getDetails())        // My name is Aditya and age is 44

The value of this will not be the object that had defined the function as his own property but the object that had call the function. In other words, if obj had defined function name getThis and we create a another object objTwo and sets it prototype to obj and then call the function getThis then the value of this would the object that had call the function.

Consider this code:

// Defined a obj with function as it's own property
const obj = {
    name: "obj",
    getThis() {
        return this
    }
}

// Defined another object and set it's prototype to "obj"
const objTwo = {
    name: "objTwo",
    age: 33
}

// set objTwo parent (or prototype) to obj
Object.setPrototypeOf(objTwo, obj)

// The "getThis" method is defined in "obj" but the "objTwo" object had called it
// so the value of "this" is "objTwo" object.
console.log(objTwo.getThis())        // {name: 'objTwo', age: 33}

The value of this always changes based on how a function is called, even when the function was defined in another object at creation:

const obj4 = {
  name: "obj4",
  getThis() {
    return this;
  },
};

const obj5 = { name: "obj5" };

obj5.getThis = obj4.getThis;
console.log(obj5.getThis()); // { name: 'obj5', getThis: [Function: getThis] }

this keyword in context of Callbacks

When a function is passed as a callback, the value of this depends on how the callback is called. Callbacks are typically called with a this value of undefined as they are called directly without attaching any object, which means if the function is non–strict, the value of this is the global object (globalThis). This is the case for iterative array methods, the Promise() constructor, etc.

In non-strict mode, when we pass callback function, the value of this is undefined in most cases and because of logic I had explained above the value of this become Global Object.

// NON-STRICT MODE

const arr = [1, 2, 3, 4, 5]

function getThis() {
    console.log(this)
}

// "getThis" function is passed as a callback to forEach array method
arr.forEach(getThis)
// It will run 5 fives and each time will log the value of this

// Iteration 1: Window {window: Window, self: Window, document: document, name: '', …} or Global object in nodejs
// Iteration 2: Window {window: Window, self: Window, document: document, name: '', …} or Global object in nodejs
// Iteration 3: Window {window: Window, self: Window, document: document, name: '', …} or Global object in nodejs
// Iteration 4: Window {window: Window, self: Window, document: document, name: '', …} or Global object in nodejs
// Iteration 5: Window {window: Window, self: Window, document: document, name: '', …} or Global object in nodejs

// "logThis" function is passed as a callback to "Promise" constructor
const promise = new Promise(function logThis(resolve, reject) {
    console.log("this in promise: ", this);
    resolve("Successful");
})

// this in promise: Window {window: Window, self: Window, document: document, name: '', …} or Global object in nodejs

In strict mode, when we pass callback function, the value of this is undefined in most cases and because of logic I had explained above the value of this does not change.

// STRICT MODE

"use strict"

const arr = [1, 2, 3, 4, 5]

function getThis() {
    console.log(this)
}

// "getThis" function is passed as a callback to forEach array method
arr.forEach(getThis)
// It will run 5 fives and each time will log the value of this
// Same for both NodeJs and browser

// Iteration 1: undefined
// Iteration 2: undefined
// Iteration 3: undefined
// Iteration 4: undefined
// Iteration 5: undefined

// "logThis" function is passed as a callback to "Promise" constructor
const promise = new Promise(function logThis(resolve, reject) {
    console.log("this in promise: ", this);
    resolve("Successful");
})

// this in promise: undefined

this keyword in context of Arrow function

The arrow function does not have there own this binding, they take the value of this from there surrounding scope (or surrounding lexical scope). Now, the word surrounding scope is the most important to keep in mind as it will help you to know what will be the value of this inside the arrow function.

Arrow functions create closure of the surrounding scope.

Key Differences from Regular Functions:

  • In regular functions, this is determined at the time of function execution, depending on who calls the function.

  • In arrow functions, this is determined at the time of function definition, based on where the function is written or defined in the code.

For example consider this code:

// In NON-STRICT MODE

// Regular function
function normal() {
    return this
}

// Arrow function
const arrow = () => {
    return this
}

console.log("The value of this in regular function: ", normal())   
// The value of this in regular function: (same for Nodejs also)
// Window {window: Window, self: Window, document: document, name: '', location: Location, …}

console.log("The value of this in arrow function: ", arrow())
// The value of this in arrow function: 
// Window {window: Window, self: Window, document: document, name: '', location: Location, …}
// {} (output empty parenthesis for node js as in it module scope is there)

By seeing this example you might think they both are giving the same value of this. Yes, they are giving the same value but there is different reason for why they both are giving same value of this.

For regular function, as I had told you that when you simply call them they does not have any context so the value of this inside them is undefined but as we are in non-strict mode it get replaced by global object due to this substitution.

For arrow function, as they do not have there own this so they take this from there surrounding scope (or surrounding lexical scope). Now, in above example the function arrow was defined in global scope in browser and module scope in node js and in browser this in global scope refer to window object and in node js this in module scope refer to {} empty parenthesis so the function arrow logs window object in browser and {} empty parenthesis in nodejs.

Now, again take this example

// In STRICT MODE

"use strict"

// Regular function
function normal() {
    return this
}

// Arrow function
const arrow = () => {
    return this
}

console.log("The value of this in regular function: ", normal())   
// The value of this in regular function: (same for Nodejs also)
// undefined

console.log("The value of this in arrow function: ", arrow())
// The value of this in arrow function: 
// Window {window: Window, self: Window, document: document, name: '', location: Location, …}
// {} (output empty parenthesis for node js as in it module scope is there)

As now, you can see in strict mode when no this substitution take place the normal function logs undefined but for arrow function as it does not have it’s own this binding so it again take this from it’s scope which is global scope and that’s why it again logs the window object in browser and {} empty parenthesis in nodejs.

this in Regular function v/s Arrow function

This section explains how the regular and arrow function behave differently.

  1. Inside an Object Method

     const obj = {
         name: "Aditya",
         age: 60,
         regular() {
             return this;
         },
         arrow: () => {
             return this
         }
     }
    
     console.log("this in regular fn: ", obj.regular())
     // this in regular fn:
     // Browser: {name: 'Aditya', age: 60, tellThisTruthInsideObject: Window Object, regular: ƒ, arrow: ƒ}
     // NodeJS: {name: 'Aditya', age: 60, tellThisTruthInsideObject: {}, regular: ƒ, arrow: ƒ}
    
     console.log("this in arrow fn: ", obj.arrow())
     // this in arrow fn:
     // Browser: Window {window: Window, self: Window, document: document, name: '', location: Location, …}
     // NodeJS: {}
    
    • I think you know the reason for the results that regular function shows. As the obj object had called the regular function so the this refer to this obj object in regular function.

    • But you might be confuse why the this inside arrow function is global object (window in browser and {} in nodejs), the value of this inside arrow function should be the reference to obj object like it was in the regular function.

    • Yes, you are right in some sense but as I told you arrow function take(or inherit) this from there surrounding scope. So, when we had defined the arrow function, the value of this in it’s surrounding scope refers to the global object so the arrow function inherits this value. For example

In this code, I have defined an obj object inside a function and we will try to set different value of this inside in function to show that arrow function defined inside the obj object take the value of this from it’s scope. Read about call here.

    function SetThisContext() {

        console.log("this in outer scope: ", this);    

        const obj = {
            arrow: () => {
                return this
            }
        }

        console.log("this in arrow fuction: ", obj.arrow())
    }

    // Both "this in outer scope" and "this in arrow fuction" refers to same value.

    SetThisContext()                         // Global Object
    SetThisContext.call("String")            // String {'String'}
    SetThisContext.call(33)                  // Number {33}
    SetThisContext.call(true)                // Boolean {true}

  1. Inside a callback function

     const obj = {
         name: "Emma",
         logName: function() {
             setTimeout(function insideSetTimeout() {
                 console.log("this in regular fn: ", this)
             }, 1000)
         },
         logNameArrow: function() {
             console.log("logNameArrow this: ", this);                // logNameArrow this: {name: 'Emma', logName: ƒ, logNameArrow: ƒ}
             setTimeout(() => {
                 console.log("this in arrow fn: ", this)
             }, 1000)
         }
     }
    
     obj.logName()                
     // this in regular fn:
     // Browser: Window {window: Window, self: Window, document: document, name: '', location: Location, …}
     // NodeJS: SetTimeout Timeout {} object
    
     obj.logNameArrow() 
     // this in arrow fn:
     // Browser: {name: 'Emma', logName: ƒ, logNameArrow: ƒ}
     // NodeJS: {name: 'Emma', logName: ƒ, logNameArrow: ƒ}
    
    • A regular function always creates it’s own this context so in logName function we have setTimeout and in it we pass regular function insideSetTimeout as a callback so it creates it’s own this which is window object in browser for same reason (context being undefined) and Timeout object in nodejs as nodejs had different implementation of setTimeout.

    • But for arrow function they take this from there scope so when we call obj.logNameArrow function, the this inside logNameArrow refer to obj so the callback function inside setTimeout is an arrow function which take this obj object as it’s this context because this is the value of this in the arrow function surrounding scope.


  1. Inside a constructor function

     function Person(name) {
       this.name = name;
    
       console.log("this inside Person: ", this);             // Person {name: 'David', sayName: ƒ, sayNameArrow: ƒ}
    
       this.sayName = function () {
         console.log(this);
       };
    
       this.sayNameArrow = () => {
         console.log(this);
       };
     }
    
     const person1 = new Person("David");
     person1.sayName();                   // Person {name: 'David', sayName: ƒ, sayNameArrow: ƒ}        
     person1.sayNameArrow();              // Person {name: 'David', sayName: ƒ, sayNameArrow: ƒ}
    

    The sayName normal function takes this from person1 as this person1 object calls him.

    The sayNameArrow arrow function takes this from its surrounding scope and inside constructor function, the this refers to the person1 object so arrow function take person1 as it’s this value.


  1. Inside a class

     class Person {
       constructor(name) {
         this.name = name;
    
         console.log("this: ", this);             // this: {name: 'Bob', arrowMethod: ƒ}
       }
    
       regularMethod() {
         console.log(this.name); // Works
       }
    
       arrowMethod = () => {
         console.log(this.name); // Works because `this` is inherited
       };
     }
    
     const person = new Person("Bob");
    
     person.regularMethod(); // {name: 'Bob', arrowMethod: ƒ}
     person.arrowMethod();   // {name: 'Bob', arrowMethod: ƒ}
    

    Same reason as the constructor

Conclusion

The this keyword in JavaScript is dynamic and depends on how a function is called. In regular functions, it is determined by the caller, in arrow function it is determined where the arrow function is defined while in strict mode, it can be undefined instead of the global object.

Reference

20
Subscribe to my newsletter

Read articles from Ridam Singhal directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Ridam Singhal
Ridam Singhal