JavaScriptのthisがよくわからないので徹底的に調べてできるだけわかりやすく解説してみる

## JavaScriptのthisがわからない?偶然！わたしもー

わたし 「あれ、このエレメントどうやって取り出すんやろ・・・せや、console.log(this)してみよ・・・」
わたし 「あれ？ undefined? なんでや？ 3行上で同じことやるとちゃんと表示されるやん・・・」

...

....

.....

......

わたし 「**なんでやねん！？thisわけわからんわ！？**」

という状況に直面したので、JavaScriptのthisをちゃんと知っておこうと思い、アウトプットもかねて記事を書くことにします。

## thisとは
thisとは、`thisが所属している関数を実行するオブジェクト`のことである。

...

...

...🤔 🤔 🤔 ？？？ (大丈夫、書いてる私も意味わかってないで）


## thisには２パターンある

thisには２パターンあります。どこで使うかによって、thisが指す内容が変わってきます。
まずこの２パターンを**今自分がなんとなくこの場でジャンプしたら地震が起きるんじゃないかくらいの「なんとなく」レベル**で理解しましょう。

### パターン ① (this = 所属しているオブジェクト)
「メソッド」の場合、thisは`「所属しているオブジェクト」そのものを指す`

ここでいうメソッドとは、「関数がオブジェクトの中に存在しているもの」のことです。

### パターン ② (this = グローバルオブジェクト)
「通常の関数(オブジェクトの中に存在していない関数）」の場合、thisはグローバルオブジェクトである`「ブラウザ内のWindowオブジェクト」&「node内のグローバルオブジェクト」を指す`

... 


....


...🤔 🤔 🤔 ？？？

わかりにくいと思うので、一つ一つ例をみていきたいと思います。

## パターン ① (メソッドでthisを使った場合はオブジェクトを返す)

「メソッド(関数がオブジェクトの中に存在しているもの)」の場合、thisは`「所属しているオブジェクト」そのものを指す`

これをなんとなく頭の片隅で意識しながら、例をみてみましょう。

### 例 ①

最初に、titleプロパティを持ったmangaオブジェクトを生成 
→ read()メソッドの中でconsole.log(this)する

```javascript
    const manga = {
      title: 'Fullmetal Alchemist',
      read() {
        console.log(this);
      }
    }
    manga.read();
```

すると、コンソールにはmangaオブジェクトが表示されます。

なぜかというと、このreadメソッドは`「mangaオブジェクトに所属している」`からです。
この場合のthisは、mangaオブジェクトそのものを指しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352778/6c77772d-6e5c-0048-9509-ec4bb526c33d.png)


### 例 ②
mangaオブジェクトの後にメソッドを追加したとしても、thisはmangaオブジェクトを参照します。

mangaオブジェクトの後に、closeメソッドを追加
→ closeメソッドの中でconsle.log(this)をする

```javascript
    const manga = {
      title: 'Fullmetal Alchemist',
      read () {
        console.log(this);
      }
    }

    manga.close = function() {
      console.log(this);
    }
    manga.close();
```

すると、thisは例①と同じような結果である、mangaオブジェクトを返します。
`closeメソッドはmangaオブジェクトのメソッド`だからです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352778/549363ae-f100-2407-a1af-b080b683853f.png)


ここまでが、メソッドの中でthisを使った場合でした。

## パターン ② (通常の関数でthisを使った場合はグローバルオブジェクトを返す)

今度は、通常の関数でthisを使った場合どうなるかみてみます。

ルールは、**「通常の関数(オブジェクトの中に存在していない関数）」の場合は、thisはグローバルオブジェクトである「ブラウザ内のWindowオブジェクト」**&**「node内のグローバルオブジェクト」を指す**、ですね。

### 例 ①

readManga()という関数を定義
→関数内でconsole.log(this)する

```javascript
    const manga = {
      title: 'Fullmetal Alchemist',
      read() {
        console.log(this);
      }
    }

    function readManga(){
      console.log(this);
    }

    readManga();
```

すると今度は、ブラウザのWindowsオブジェクトが表示されました！
「Window」から始まるのがWindowsオブジェクトであることを指してますね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352778/a0e7dec6-1d6f-d910-62be-13272069388e.png)
このように関数の中でthisを使うと、thisはWindowオブジェクトを指すことがわかります。

### 例 ② コンストラクタ関数の場合はちょっと特殊

コンストラクタ関数の場合はちょっと特殊。thisはオブジェクトを指すようになります。

manga関数を生成
→ newして新しいmangaオブジェクトを作る

```javascript
    const manga = {
      title: 'Fullmetal Alchemist',
      read() {
        console.log(this);
      }
    }

    function Manga(title){
      this.title = title;
      console.log(this);
    }

    // 新しいmangaオブジェクトを生成後、空っぽのオブジェクトを生成する -> {}
    const doraManga = new Manga('Doraemon'); 
   

```

すると今度は、Windowオブジェクトの代わりに、**「mangaオブジェクト」が返されました。**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352778/b7f3d25b-9199-ad5a-1bbc-0ca63abecb6e.png)

#### なぜ？
new演算子は「空っぽのオブジェクト」を生成します。そして、「this」をその空っぽのオブジェクトの中に放り込む仕組みです。
そのため、この「doraManga（生成されたmangaオブジェクト）」は、1行目〜６行目のmangaオブジェクトは**全く別物**になります。

結果、Windowオブジェクトではなく、mangaオブジェクトが表示されました。




### 例 ③

今度は下記をやってみたらどうなるか見てみます。

categoriesというプロパティを作って、配列を当てはめる
→ showCategoriesメソッドを作る 
→ メソッド内でthis.categories.forEachし、その中でconsole.log(categories)する

```javascript
    const manga = {
      title: 'Fullmetal Alchemist',
      categories: ['love', 'philosophy', 'adventure'],
      showCategories() {
        this.categories.forEach(function(categories){
          console.log(categories);
        });
      }
    };

    manga.showCategories();
```

すると、こんな風にcategoriesが表示されます。ここまでは予想通り、オッケーですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352778/60f962fe-6994-db18-3cb1-9d586efd9a36.png)

でももし、**titleをそれぞれのcategoriesの横に表示させたい**としたら、どうすればいいでしょうか？

試しに、this.titleしてみましょう。

```javascript
    const manga = {
      title: 'Fullmetal Alchemist',
      categories: ['love', 'philosophy', 'adventure'],
      showCategories() {
        this.categories.forEach(function(categories){
          console.log(this.title, categories);
        });
      }
    };

    manga.showCategories();
```

undefinedになっちゃう...><;
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352778/5f27ef4a-9af4-ca53-6097-371a300439ae.png)


this.titleをthisにすると...？

```javascript
    const manga = {
      title: 'Fullmetal Alchemist',
      categories: ['love', 'philosophy', 'adventure'],
      showCategories() {
        this.categories.forEach(function(categories){
          console.log(this, categories);
        });
      }
    };

    manga.showCategories();
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352778/1e0232b3-f6cc-530e-d180-1a9fc8b28487.png)

Windowオブジェクトが返されました！

**「このthisは「mangaオブジェクト」の中にいるから、mangaオブジェクトを指すべきでは？なぜwindowオブジェクを指しているの？」**と思うかもしれません。

このthisは、forEachというcallback関数の中にいますね。この関数は**通常の関数**です。要するに、mangaオブジェクトのメソッドではありません。

結果、ルール２が適用されてthisはグローバルオブジェクトを指すのです。




#### でも、どうやってmangaのタイトルを、categoriesの隣に表示すれば良いの？🤔

今回は、forEachメソッドを使っているので、２つめの引数を設定することができます。
第一引数にcallback関数、第二引数に「this」を入れてあげることで、thisがmangaオブジェクトを指してくれます。

#### 例えばこんな風に

```javascript
    const manga = {
      title: 'Fullmetal Alchemist',
      categories: ['love', 'philosophy', 'adventure'],
      showCategories() {
        this.categories.forEach(function(categories){
          console.log(this, categories);
        }, this);
      }
    };

    manga.showCategories();
```

だだーん。
グローバルオブジェクトではなく、mangaオブジェクトが表示されました。

この場合、thisはforEachメソッドの中ではなく、`showCategoriesメソッドに所属しています。`
そのため、mangaオブジェクトを指しているのです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352778/ed8c5f1a-3f19-cdca-23a8-1c85507d05bb.png)

#### では、thisをthis.titleに変えると？

```javascript
    const manga = {
      title: 'Fullmetal Alchemist',
      categories: ['love', 'philosophy', 'adventure'],
      showCategories() {
        this.categories.forEach(function(categories){
          console.log(this.title, categories);
        }, this);
      }
    };

    manga.showCategories();
```

きれい！満足や！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352778/27cf24a9-88e3-ce28-cc80-37766c6361a7.png)

### ただし
forEachのようにすべてのJavaScriptのメソッドが、第２引数を入れてあげられるわけではないので、注意！

### ちなみに
こんな書き方もできます。この書き方が一番簡単かもしれませんね。

```javascript
  const manga = {
      title: 'Fullmetal Alchemist',
      categories: ['love', 'philosophy', 'adventure'],
      showCategories: function() {
        this.categories.forEach((category) => {
          console.log(this, category);
        });
      }
    };

    manga.showCategories();
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352778/9691fcae-a68e-4c58-5166-5ed8de510b17.png)


##まとめ
これで君をJavaScript this使いに任命する！

P.S　私は鋼の錬金術師の大ファンです。