blog thumnail

Understanding this in JavaScript

javascriptthis

Tanveer Sayem / 2022-10-30

5 min read

One of the confusing topics in JavaScript is the 'this' keyword. A function's 'this' keyword behaves a little differently in JS compared to other languages. It also has some differences between strict mode and non-strict mode. Let's learn all about them in this article.
this in JavaScript is a reserved keyword. In most cases, the value of this is determined by how a function is called(runtime binding). It can't be set by assignment during execution, and it may be different each time the function is called. The bind() method can set the value of a function's this regardless of how it's called, and arrow functions don't provide their own this binding. Let's see how this behaves in different contexts.
  1. Global context In the global execution context (outside of any function), this refers to the global object whether in strict mode or not. We can see this by simply typing this in the devtools of the browser.
    this // Window {0: Window, window: Window, self: Window,...,
    We can also run console.log(this === window) // true
  2. Function context Now, if we log this inside a function, we are going to get two different outputs based on if we are in strict mode or not.
    function myFunc() {
        console.log(this);
    }
    myFunc(); // Window {0: Window, window: Window, self: Window,...,
    So in a non-strict mode, we are getting the same global object in the case of browsers - it is window object like before. In JavaScript this first looks for the value in the function curly braces, when it does not find any value there, then it goes outside of the function scope and looks for the value which is in the global context of the global object. This behavior of this could be a source of a bug. So, in order to solve the issue, we can use use strict
    function myFunc() {
       'use strict';
       console.log(this);
    }
    myFunc(); // undefined
    Let's get another example:
    function someFunc() {
       this.name = 'Rahim Shah';
    }
    someFunc();
    window.name; // 'Rahim Shah'
    As you can see from the above example this.name attached the name with the global object which is the window object in the browser. In case of strict mode it will throw a TypeError: Cannot set properties of undefined
  3. As an object method When a function is called as a method of an object, it's this is set to the object the method is called on. In the following example, when customObj.message() is invoked, inside the function this is bound to the customObj object.
    const customObj = {
        name: 'Rahim',
        occupation: 'Developer',
        message: function() {
           console.log('Hey! my name is', this.name + ' and I am a ' + this.occupation);
      }
    }
    customObj.message(); // 'Hey! my name is', Rahim and I am a Developer'
    
    this binding is only affected by the most immediate member reference. In the following example, let's call a function in a nested object.
    const customObj = {
       name: 'Rahim',
       occupation: 'Developer',
       nestedObj: {
           name: 'Karim',
           occupation: 'Engineer',
           message: function() {console.log('Hi, my name is ' + this.name);}
       }
    }
    
    If we call customObj.nestedObj.message(); we will get 'Hi, my name is karim' back.
  4. Class context The behavior of this in classes and functions is similar since classes are functions under the hood. Within a class constructor, this is a regular object. All non-static methods within the class are added to the prototype of this:
    class Example {
      constructor() {
         const proto = Object.getPrototypeof(this);
         console.log(Object.getOwnPropertyNames(proto));
      }
      first(){}
      second(){}
      static third(){}
    }
    
    new Example(); // ['constructor', 'first', 'second']
    
    Note: Static methods are not properties of this. They are properties of the class itself.
  5. The bind(), call(), apply() method We can specifically bind this to an object with the call(), or apply() method. For an example let's take our previous example of nested object.
    const customObj = {
       name: 'Rahim',
       occupation: 'Developer',
       nestedObj: {
           name: 'Karim',
           occupation: 'Engineer',
           message: function() {console.log('Hi, my name is ' + this.name);}
       }
    }
    
    If we run customObj.nestedObj.message() we are going to get the name printed as 'karim' but what if we want the 'Rahim' name instead? We can use the call() method to achieve the desired result.
    customObj.nestedObj.message.call(customObj) // Hi, my name is Rahim
    
    apply() method will get the same result as above. Let's look at the bind() method. Calling someFunc.bind(someObj) creates a new function with the same body and scope as someFunc, but where this occurs in the original function, in the new function it is permanently bound to the first argument of bind, regardless of how the function is being used.
    function someFunc() {
       return this.property;
    }
    const anotherObj = someFunc.bind({ property: 'some value'})
    console.log(anotherObj()) // some value
  6. Arrow functions In arrow functions, this will be set to the global object in the global code. In the following code, this will return undefined:
    const customObj = {
       name: 'Rahim',
       nestedObj = {
          name: 'Karim',
          msg: () => {console.log(this)}
       }
    }
    If we run customObj.nestedObj.msg() will return window object in browsers.
If you like to learn more: this - MDN
If you click on my affiliates links and shop(anything, not just books), I am going to receive a tiny commission. AND… Most of the time, you will receive an offer. Win/Win! The products that I have are the ones I believe in.
amazon

Subscribe to the newsletter

Get emails from me about web development, tech, and early access to new articles.


  • Home
  • About
  • Newsletter
  • Twitter
  • Github
  • YouTube
  • Setup
  • Guestbook
  • Snippets