The "this" Keyword in JavaScript

ankitat721

Ankita Talukdar

Posted on August 31, 2023

The "this" Keyword in JavaScript

Introduction

Understanding the behavior of the "this" keyword in JavaScript is crucial for every developer. It often becomes a little confusing. Let's start the discussion with the very basic idea of this keyword.

As stated in the MDN documentation, "A function's this keyword behaves a little differently in JavaScript compared to other languages. It also has some differences between strict mode and non-strict mode."

Here, you are going to understand what is "this" in JavaScript. We will unravel the mysteries of "this" and explore its various use cases, binding rules, and best practices.

First we are going to understand "this" with a very simple example, then we will witness all the explanation and examples of the terms used in the previous paragraphs. Here goes the first example:

const person = {
  name: "Alex",
  profession: "teacher",
  greet: function () {
    console.log(`Hello, my name is ${this.name} and I am a ${this.profession}.`);
  },
};

person.greet(); 

Enter fullscreen mode Exit fullscreen mode

In this code snippet, the person object has three properties: name, profession, and greet which is a method (method is an object property that has a function value). And then we are calling the greet method with person.greet(). The output we get after performing this is:

Hello, my name is Alex and I am a teacher.
Enter fullscreen mode Exit fullscreen mode

In this case, the this keyword refers to the person object. So, when the method greet is invoked using person.greet(), this inside the greet method points to the person object itself.

The this keyword allows us to access and use the properties of the object in which the method is defined. We can have multiple objects with the same method, and each object can refer to its own properties using this.

In simple terms, this refers to the context of the current object when a method is called.

Context and Execution

In JavaScript, this is a context-dependent keyword that refers to the current execution context.

Now, what is execution context?

In JavaScript, there are two main types of execution context:

Global Context: It is the default context in which code is executed outside any function. In the global context, this refers to the global object (e.g. "window" in browsers.)

Function Context: It is the context within the execution of a function. Functions in JavaScript run in a specific context, and using the this keyword we can access it. Here, the value of this is determined by the function invocation pattern.

Function Invocation

Function invocation or in simple words calling a function in JavaScript has a few different ways, they are

Simple Function Invocation: In non-strict mode of JavaScript, when a function is called or invoked, this keyword refers to the global object which is the "window" in the browser and "global" on Node.Js. But in strict mode, JavaScript sets this to undefined. (Stict and non-strict mode will be discussed in later in this article). Here is the example:

function example() {
  console.log(this); // Points to the global object (window or global)
}
example();
Enter fullscreen mode Exit fullscreen mode

Method Invocation: When a function is called as a method of an object, this simply refers to the object that owns the method. We can take the same example as the first one, which is:

const person = {
  name: "Alex",
  profession: "teacher",
  greet: function () {
    console.log(`Hello, my name is ${this.name} and I am a ${this.profession}.`);
  },
};

person.greet();

Enter fullscreen mode Exit fullscreen mode

Here, greet is a method and inside greet, this keyword is referring to the object person which owns the method greet.

Constructor Invocation: When a function is invocated using the new keyword that is when a function is used as a constructor with the new keyword, it refers to constructor invocation.

Here, this refers to the newly created instance of the object.
Let's see an example to have a clear understanding:

function person(arg1, arg2) {
  this.firstName = arg1;
  this.lastName  = arg2;
}

const personName = new person("Alex", "Miller");

console.log(personName.firstName);

Enter fullscreen mode Exit fullscreen mode

Here, person is a constructor function that takes two arguments (arg1 and arg2). While calling this function with the new keyword, it creates a new object personName and sets the firstName and lastName properties on that object using the values of arg1 and arg2. The this keyword inside the constructor function refers to the newly created object.

Indirect Invocation: Indirect invocation, also known as function borrowing or using methods like call() and apply(), allows you to invoke a function with a specific context (the this value) different from its original context. This technique is useful when you want to reuse a function but apply it to another object temporarily.

Here's an example:

function introduce() {
  console.log(`My name is ${this.name} and I am a ${this.profession}.`);
}

const person1 = {
  name: "Alex",
  profession: "singer",
};

const person2 = {
  name: "Robert",
  profession: "teacher",
};

introduce.call(person1); // Output: My name is Alex and I am a singer.
introduce.call(person2); // Output: My name is Robert and I am a teacher.

Enter fullscreen mode Exit fullscreen mode

In this example, the introduce() function is defined. Instead of defining the same function for each person object, the call method can be used to "borrow" the function and set a specific context (the person object) as this.

Similarly, the apply method is used to achieve the same result, but it takes an array of arguments:

function greet(greeting) {
  console.log(`${greeting}, ${this.name}!`);
}

const person = {
  name: "Alex",
};

greet.apply(person, ["Hello"]); // Output: Hello, Alex!

Enter fullscreen mode Exit fullscreen mode

Both call and apply methods provide a way to temporarily change the this context when invoking a function. This can be useful while reusing a function with different data contexts.

Binding

Binding the this keyword is a fundamental concept in JavaScript, as it determines the context in which a function is executed.

Default Binding: When a function is invoked in the global scope (outside any other function or object), this refers to the global object (window in browsers). However, in strict mode, this will be undefined instead of pointing to the global object.

Implicit Binding: When a function is called as a method of an object, this points to the object itself. The object to the left of the dot during the function call becomes the context of this.
Let's see an example:

const person = {
  name: "Alex",
  greet: function () {
    console.log(`Hello, I'm ${this.name}.`);
  },
};

person.greet(); // Output: Hello, I'm Alex.

Enter fullscreen mode Exit fullscreen mode

Explicit Binding: Developers can explicitly set the value of this using the call, apply, or bind methods on functions.

  • The call method immediately invokes the function with the specified context and arguments.
  • The apply method is similar to call, but it takes arguments as an array.
  • The bind method returns a new function with the specified context and, optionally, pre-set arguments(will be discussed in detail shortly).
function greet(message) {
  console.log(`${message}, I'm ${this.name}.`);
}

const person = {
  name: "Robert",
};

greet.call(person, "Hi"); // Output: Hi, I'm Robert.
greet.apply(person, ["Hey"]); // Output: Hey, I'm Robert.

const boundGreet = greet.bind(person, "Hello");
boundGreet(); // Output: Hello, I'm Robert.

Enter fullscreen mode Exit fullscreen mode

Arrow Functions and This Keyword

The behavior of the this keyword in arrow functions is one of the key distinctions between arrow functions and regular (non-arrow) functions in JavaScript.

No Separate this Binding: Arrow functions do not have their own this binding. Unlike regular functions, which bind
this based on how they are called, arrow functions inherit the this value from their surrounding code block (lexical scoping).

Lexical this Context: The value of this in an arrow function is determined by the value of this in the nearest enclosing non-arrow function scope. In other words, it retains the this value from the containing function or scope where the arrow function is defined.

Let's understand it with an example:

const obj = {
  name: "Alex",
  regularGreet: function () {
    console.log(`Hello, I'm ${this.name}.`);
  },
  arrowGreet: () => {
    console.log(`Hi, I'm ${this.name}.`);
  },
};

obj.regularGreet(); // Output: Hello, I'm Alex.
obj.arrowGreet();   // Output: Hi, I'm undefined.

Enter fullscreen mode Exit fullscreen mode

In the regularGreet() method, the this value is correctly bound to the obj object because it's a regular function, and the context is determined by how the method is invoked.

However, in the arrowGreet() method, the this value is undefined because arrow functions do not have their own this binding. Instead, they inherit this from the surrounding lexical scope, which in this case is the global scope. This is why this.name inside the arrow function resolves to undefined.

const person = {
  name: "Robert",
  greet: function () {
    const innerFunction = () => {
      console.log(`Hi, I'm ${this.name}.`);
    };
    innerFunction();
  },
};

person.greet(); // Output: Hi, I'm Robert.

Enter fullscreen mode Exit fullscreen mode

Here, the arrow function, innerFunction() has a lexical this context and inheriting this from the surrounding code block.

Arrow functions are particularly useful in scenarios where someone wants to maintain the this value from the enclosing scope. They're commonly used in callback functions, such as event handlers, to retain access to the parent context without the need for additional workarounds like binding.

The bind() Method

The bind() method in JavaScript is used to create a new function that, when invoked, has its this value set to a specific value, regardless of how the function is called. This can be especially useful to ensure a specific context for a function, such as when passing a function as a callback to an event handler.

Let's have a look at an example:

const obj = {
  name: "Alex",
};

function greet() {
  console.log(`Hello, I'm ${this.name}.`);
}

const boundGreet = greet.bind(obj);

boundGreet(); // Output: Hello, I'm Alex.

Enter fullscreen mode Exit fullscreen mode

In the example above, the greet() function is defined, and an object obj is also defined. Using the bind() method, a new function boundGreet() is created. When boundGreet() is invoked, it maintains the this value of the obj object, regardless of where it's called.

The bind() method also allows to provide additional arguments that will be pre-set when the bound function is invoked.

function say(message) {
  console.log(`${message}, I'm ${this.name}.`);
}

const person = {
  name: "Robert",
};

const boundSay = say.bind(person, "Hello");

boundSay(); // Output: Hello, I'm Robert.

Enter fullscreen mode Exit fullscreen mode

Here, the bind() method is used to create the boundSay() function with the context of the person object and the message "Hello" already set. When boundSay() is called, it logs "Hello, I'm Robert."

The bind() method doesn't modify the original function instead creates a new function with the specified context and pre-set arguments.

Strict Mode vs. Non-Strict Mode

In ECMAScript 5, the introduction of strict mode ("use strict") brought some changes to the behavior of the "this" keyword:

In strict mode, if a function is called in the global scope, the default binding of "this" is not the global object. Instead, it is set to undefined. This avoids accidentally modifying global variables unintentionally.
If a function is called without any specified context, "this" remains undefined inside the function, preventing implicit global binding.

In strict mode, when using call, apply, or bind to set "this" explicitly, if the specified "this" value is a primitive (e.g. a string or a number), it will be converted to an object. This prevents errors that could occur when attempting to access properties on primitive values.

In non-strict mode, if a function is called without any specified context, the global object will be the default value of "this".

Understanding the intricacies of the "this" keyword and being aware of strict mode can help developers write more reliable and maintainable JavaScript code.

Conclusion

The "this" keyword in JavaScript is a dynamic element that plays a crucial role in determining the execution context of functions. Its behavior can vary based on how functions are invoked and whether you're in strict or non-strict mode. Throughout this blog, we've explored the different ways "this" is bound and how it impacts your code.

While "this" might be a source of confusion initially, continuous practice and exploration will lead to mastery. "this" empowers one to create more versatile and effective scripts.

💖 💪 🙅 🚩
ankitat721
Ankita Talukdar

Posted on August 31, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

The "this" Keyword in JavaScript
javascript The "this" Keyword in JavaScript

August 31, 2023