The "this" Keyword in JavaScript
Tags: javascript, this, webdev, tutorial
Published: 2023-08-31 16:49:28


## 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:

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

person.greet(); 

```
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: 

```javascript
Hello, my name is Alex and I am a teacher.
```
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:

```javascript
function example() {
  console.log(this); // Points to the global object (window or global)
}
example();
```

**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:

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

person.greet();

```
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:

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

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

console.log(personName.firstName);

```
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: 

```javascript
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.

```
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:

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

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

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

```
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:

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

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

```
**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).

```javascript
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.

```

## 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:

```javascript
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.

```
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`.

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

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

```
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:

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

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

const boundGreet = greet.bind(obj);

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

```
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.

```javascript
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.

```
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.