JavaScript: this
Published: 2022-07-19 18:04:31

# JavaScript: All about JS keyword _this_

## Intro

As a frontend engineer, _this_ is a keyword that you will see very often. It is a pretty fundamental concept, yet an extremely important onf if you want to become better at javascript, or as a frontend engineer. In this article, I am going to cover all the knowledge that is entailed to _this_.

## global environment

For convenience, we'll refer global environment as `ge` below.

Basically, if you call `this` directly in `ge`, you will get a `window` object. And it is strictly equal.

```js
this === window; // true
```

And of course, the code below remains true:

```js
this.document === document;
```

Furthermore, if you try to add a new attribute to `window` directly by `this`, it is also feasible. For example:

```js
this.myName = "Tris";
console.log(myName); // Tris
console.log(window.myName); // Tris

this.myName === window.myName; // true
this.myName === myName; // true
```

Even if you apply the strict mode by adding `use strict` at the top of file, it still behaves as if `this` basically points to the `window` object.

### functional environment

For convenience, we'll refer functional environment as `fe` below.

In `fe`, if you call `this`, its reference depends on how you call this function. If you call the function directly, `this` will refer to the `window` object directly, this is called a `Sample Call`.

For example:

```js
function fn() {
  return this;
}

fn() === window; // true
```

However, most of the time when we're actually developing, we have a preference to avoid calling a function directly(`Sample Call`). Take an example of Vue developing:

```js
const app = new Vue({
  el: "#app",
  data: {
    myName: "Ray",
  },
  methods: {
    getName() {
      console.log(this.myName); // Ray
    },
  },
  created() {
    this.getName();
  },
});
```

Looks just fine, right? But let's see what will happen by just changing the code a little bit:

```js
const app = new Vue({
  el: "#app",
  data: {
    myName: "Ray",
  },
  methods: {
    getName() {
      const array = [1, 2, 3];
      array.forEach(function () {
        console.log(this.myName); // undefined * 3 ，WTF?
      });
    },
  },
  created() {
    this.getName();
  },
});
```

Now, that's very weird right? Why does a simple `forEach` result in such bizarre outputs? Well, let's first take a look at `forEach` source code:

```js
Array.prototype.forEach = function(callback) {
    for(let index = 0; index < this.length(); index++> {
        callback(this[index], index, this);
    })
}
```

We can see that the function passed to `forEach` is `sample called`, resulting in a conflict of `this`, therefore the reason of `this` reference misdirect.

How will we deal with this problem, or if we put it in other words, what is a better way of programming in order to prevent and elude these strange confusing `this` reference problems?

The first way is to declare a variable to reserve `this`.

```js
const app = new Vue({
  el: "#app",
  data: {
    myName: "Ray",
  },
  methods: {
    getName() {
      const vm = this;
      const array = [1, 2, 3];
      array.forEach(function () {
        console.log(vm.myName); // Ray * 3，is Good!
      });
    },
  },
  created() {
    this.getName();
  },
});
```

The second way, it to use the `arrow function`.

```js
const app = new Vue({
  el: "#app",
  data: {
    myName: "Ray",
  },
  methods: {
    getName() {
      const array = [1, 2, 3];
      array.forEach(() => {
        console.log(this.myName); // Ray * 3，is Good!
      });
    },
  },
  created() {
    this.getName();
  },
});
```

By either way, the problem seems to be solved. For the first way, by creating a variable to store `this` in the Vue component separates different `this` references under different enrivironment. But the second way seems somewhat confusing. Why do a simple `arrow function` takes care of the problem? Don't worry, everything will be explained later.

So basically, when you call a function directly, or even pass an anonymous function as a parameter, it is often a `sample call`, which might result in `this` problems.

```js
const app = new Vue({
  el: "#app",
  data: {
    myName: "Ray",
  },
  methods: {
    getName() {
      (function () {
        console.log(this.myName); // undefined，WTF...
      })();
    },
  },
  created() {
    this.getName();
  },
});
```

## object function

Another very common case is when we put `this` in a javascript object's function. This is also a very dangerous spot that may lead to a `this` reference misdirect problem.

```js
var myName = "oh No!";

var obj = {
  myName: "Tristan",
  fn: function () {
    console.log(this.myName);
  },
};

obj.fn(); // Tristan

var fn = obj.fn;
fn(); // oh No!，WTF?
```

Notice that the output kind of makes no sense as you call the same function `fn` in object `obj`. Notice that we used `var` instead of `let`, `const`.

If we use `let` or `const`:

```js
const myName = "oh No!";

const obj = {
  myName: "Ray",
  fn: function () {
    console.log(this.myName);
  },
};

obj.fn(); // Ray

var fn = obj.fn;
fn(); // undefined，WTF?
```

In this article we're not really gonna spend too much time explaining the differences between `var`, `let` and `const`, which we will in the future.

To find out `this` references in a js object is pretty simple though. You just need to look which object if `this` in when called. Take `fn()` above as an example. `this` is called under the object `obj`, hence `this` refers to `obj`.

### new constructor

When we use `new` before the function that we are calling, in javascript, it treats the function as if it's an object, where `this` becomes a basic property of the object.

```js
function fn(myName) {
  this.myName = myName;
}

const newFn = new fn("Tris");

console.log(newFn.myName); // Tris
```

Just like an plain js object:

```js
const obj = {
  myName: "Tris",
};

console.log(obj.myName); // Tris
```

### DOM

When you use `this` on DOM elements, when you use it with `addEventListener`, `this` will then refer to the DOM element itself on matter what.

For example:

```html
<button type="button" class="btn">1</button>
<button type="button" class="btn">2</button>
<button type="button" class="btn">3</button>
<button type="button" class="btn">4</button>
<button type="button" class="btn">5</button>
```

```js
const buttons = document.querySelectorAll(".btn");

buttons.forEach(function (button) {
  button.addEventListener("click", function () {
    console.log(this); // The DOM you click
  });
});
```

When you declare an event listener by adding `addEventListener` on a specific DOM element, it actually is going to wait in the future at some point when you conduct a certain act, such as `click` in this case. Meantime, the callback function will be binded to the DOM element, so basically, you can think if the code above as below, if it helps to understand:

```js
const click = {
  button: '<button type="button" class="btn">1</button>',
  addEventListener() {
    console.log(this);
  },
};

click.addEventListener();
```

However, when using `this` with DOM elements, sometimes you find `this` refering to strange things. Which is when you use it with `arrow functions`.

```js
const buttons = document.querySelector(".btn");

buttons.forEach(function (button) {
  button.addEventListener("click", () => {
    console.log(this); // window ???
  });
});
```

Well, you might remember that above you've just read about `arrow functions`. Actually, `arrow functions` indeed include some important points that has to do with `this`, so let's continue this `this` topic by taking a look at `arrow functions`.

### arrow function expressions

Before we actually talk about arrow functions, we have to understand the differences between `arrow functions` and `normal functions`.

In MDN docs, it is said that an arrow function does not have its `this`, `arguments`, `super` and `new.target`. So, the question is, if arrow functions do not have their own `this`, then where does `this` refer to when using arrow functions.

It turns out that when using arrow functions, `this` will refer to its outer domain, the parent
domain.

Let's take a look again at an example which seems confusing at first:

```js
const app = new Vue({
  el: "#app",
  data: {
    myName: "Ray",
  },
  methods: {
    getName() {
      const array = [1, 2, 3];
      array.forEach(() => {
        console.log(this.myName); // Ray * 3，is Good!
      });
    },
  },
  created() {
    this.getName();
  },
});
```

In the code above, if we write is as `array.forEach(function() {...})`, the output would be `undefined` or `window` depending on the environment you're working with. The reason is that this results in a `sample call` as previously mentioned. However, by using arrow functions, due to the absent of `this` of arrow functions' inherit nature, it then follows its way up to refer to the nearest `this`, which turns out to be the vue instance in this case.

## references

| name     | link                                                                                        |
| :------- | :------------------------------------------------------------------------------------------ |
| MDN docs | https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Operators/this            |
| MDN docs | https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Functions/Arrow_functions |