Demystifying the `this` keyword in JavaScript
Tags: javascript, webdev, programming, tutorial
Published: 2024-07-07 21:29:01

There is a lot of confusion around the `this` keyword in JavaScript. The way `this` is described in the official [MDN web docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this) is not very intuitive. If you feel similarly, let's learn about `this` keyword using some practical examples. 

## What is `this` ?

The `this` keyword refers to the context where a piece of code should run. If we console log the following in our browser what do we get?

```js
console.log(this)
```

The result of running the above code is the entire browser `Window` object. If you run it in Node instead, you get the *global* object. Therefore, `this` represents the context of where the code is defined.

Let's take a look at an example. Below we have an object called `student` and inside the object we will define a function called `testFunction`. 

```js
const student = {
  name: "Jon",
  phone: 415887231,

  testFunction: function () {
    console.log(this);
  },
};
student.testFunction();
```

Output: 

```json

{
	name: "Jon"
	phone: 415887231
	testFunction: ƒ testFunction()
}
```

We are console logging `this` from inside the `testFunction`. Since `testFunction` is defined inside the `student` object we get the entire `student` object as an output. The `student` object is the context where the function is defined.

## `this` and the `strict` mode in Node

When you are using the strict mode in Node the behaviour of `this` is different. The following example demonstrates the behaviour. 

```jsx
'use strict';

// Top level
console.log(this);  // {}

function strictFunction() {
    console.log(this);  // undefined
}

strictFunction();
```

In the top level in a module `this` is still the *global object*. However, inside a function `this` is now *undefined*. 

Why would this be useful? There are many use cases of the `strict` mode. The following are some of them. 

**Avoiding accidental Globals:**

```jsx
'use strict';
x = 10; // ReferenceError: x is not defined
```

Assigning a value to an undeclared variable creates a global variable. Strict mode prevents this.

**Duplicates in object literals:**

```jsx
'use strict';

var obj = {
    prop: 1,
    prop: 2 // SyntaxError: Duplicate data property in object literal
};

```

Strict mode throws an error for duplicate property names in object literals.

**Catch silent errors:**

In non-strict mode, assigning a value to a non-writable property does nothing silently. In strict mode, it throws an error.

```jsx
// non strict mode
var obj = {};
Object.defineProperty(obj, 'x', { value: 42, writable: false });

obj.x = 9; // Fails silently, obj.x remains 42
console.log(obj.x); // 42
```

```jsx
'use strict';

var obj = {};
Object.defineProperty(obj, 'x', { value: 42, writable: false });

obj.x = 9; // TypeError: Cannot assign to read only property 'x' of object '#<Object>'
```

## How does `this` behave inside regular functions and arrow functions?

Understanding how `this` behaves in different scenarios is important for writing clean and predictable JavaScript code, especially when dealing with object methods and callbacks.

The following example shows the behaviour of `this` in a regular callback function that is defined inside a function.

```jsx
const student = {
  name: "Jon Doe",
  printAllCourses: function () {
    const subjects = ["cs-101", "cs-202", "econ-101"];
    subjects.forEach(function (sub) {
      console.log(sub);
      console.log("Student Name", this.name);
    });
  },
};

student.printAllCourses();

// outputs

/**
cs-101
Student Name undefined
cs-202
Student Name undefined
econ-101
Student Name undefined
**/
```

Notice the callback function for `forEach` is trying to access the `name` property but is returning *undefined*. 

Now turn the callback function into an arrow function and run the code again.

```jsx
const student = {
  name: "Jon Doe",
  printAllCourses: function () {
    const subjects = ["cs-101", "cs-202", "econ-101"];
    subjects.forEach((sub) => {
      console.log(sub);
      console.log("Student Name", this.name);
    });
  },
};

student.printAllCourses();

// outputs

/**
cs-101
Student Name Jon Doe
cs-202
Student Name Jon Doe
econ-101
Student Name Jon Doe
**/
```

Notice that this time it is able to access the name property as expected. 

What’s actually happening here?

**`this` inside a regular function** depends on how the function is called. When a regular function is used as a callback, only then `this` refers to the global object (`window` in browsers), which is the case in the above example. Alternatively, if the function is not called as a callback function it can access the parent objects' properties using `this`. So, basically, a callback function is always treated as a function that is defined in the global context.

**Arrow functions, on the other hand**, do not have their own `this` context. Instead, they lexically inherit `this` from the surrounding code. This simply means that the arrow function has access to the properties of the object it is defined in. So `this` inside an arrow function is the same as `this` outside the arrow function.

```js
const student = {
  name: "Jon Doe",
  printAllCourses: function () {
    const subjects = ["cs-101", "cs-202", "econ-101"];
    console.log("outside arrow func", this); // this here is the same this inside the callback arrow function below
    subjects.forEach((sub) => {
      console.log("inside arrow func", this); // same this as above
    });
  },
};
student.printAllCourses();
```

Hey if you are enjoying this post and want to see more like it don't forget to give me a follow on dev.to, [X](https://twitter.com/HaqueShadid) or [LinkedIn](https://www.linkedin.com/in/shadid-haque/)

## How does `this` work  in es6 class context?

In ES6 classes, `this`  keyword is more predictable and consistent compared to traditional JavaScript functions. When using `this` inside methods of a class, it generally refers to the instance of the class on which the method was called.

Consider a simple ES6 class:

```jsx
class Person {
  constructor(name) {
    this.name = name;
  }

  printName() {
    console.log(this.name);
  }
}

const person = new Person('Alice');
person.printName(); // Outputs: Alice

```

In the example `this` inside the `constructor` method refers to the new instance of the class being created. 

`this` inside the `printName` method refers to the instance of the `Person` class on which the `printName` method was called.

Now let’s take a look at an example that shows potential issues when using class methods as callbacks and how to address them:

```jsx
class Person {
  constructor(name) {
    this.name = name;
  }

  printName() {
    console.log(this.name);
  }

  printNameDelayed() {
    setTimeout(function() {
      console.log(this.name);
    }, 1000);
  }

  printNameDelayedWithArrow() {
    setTimeout(() => {
      console.log(this.name);
    }, 1000);
  }

  printNameDelayedWithBind() {
    setTimeout(function() {
      console.log(this.name);
    }.bind(this), 1000);
  }
}

const person = new Person('Bob');
person.printName(); // Outputs: Bob
person.printNameDelayed(); // Outputs: undefined (or error in strict mode)
person.printNameDelayedWithArrow(); // Outputs: Bob
person.printNameDelayedWithBind(); // Outputs: Bob

```

Notice, in **`printNameDelayed`** method the callback function passed to `setTimeout` is a regular function, so `this` inside it does not refer to the instance of `Person`. Instead, it refers to the global object (or `undefined` in strict mode).

Using an arrow function as the callback preserves the `this` context from the enclosing method, so `this` refers to the instance of `Person`. The `printNameDelayedWithArrow` is a possible solution to the callback problem. 

Another way to solve this callback problem is through binding. In `printNameDelayedWithBind` we explicitly bind the callback function to `this` . It ensures that `this` inside the callback refers to the instance of `Person`.

## Final words

I hope, this article gave you a better understanding of `this`.
 
Think of this as JavaScript's mischievous trickster—always keeping you on your toes. But now, you're in on the joke. Keep experimenting and soon this will be your trusty sidekick, not your nemesis.

So next time someone says JavaScript is confusing, just wink and say, "I know exactly who `this` is!" Go forth and conquer, and may the `this` be with you!

If you enjoyed this post give me a follow on [X](https://twitter.com/HaqueShadid) or [LinkedIn](https://www.linkedin.com/in/shadid-haque/)