ゲームを作りながら学ぶ！JavaScript レベルアップ講座 part3

こんにちは、[Yui](https://twitter.com/yui_active)です。

今回は[前回](https://qiita.com/Yui_active/items/6059fa1b564f085d27d4)に引き続きJavaScript道場で学んだthisについて解説をしていきます。

# thisとは何かを理解する

まずは前回使ったこちらのコードを見てください。

```js
var personA = {
  name: 'Bob',
  greeting: function() {
    alert('Hi! I\'m ' + this.name + '.');
  }
}
```

この中のthisが何なのかを確認する前に、thisを抜いたらどうなるのかを確認してみましょう

```js
var personA = {
  name: 'Bob',
  greeting: function() {
    alert('Hi! I\'m ' + name + '.');
  }
}
```
この状態で```personA.greeting()```を見てみると、アラートには```Hi! I'm .```と表示されます。
つまり、Bobという名前が引き継がれていないことがわかります。
それはなぜかというと、**thisがないとグローバル変数を参照してしまう**からです。

もう少しわかりやすく書きます。

```js
var name = 'Sam';
var personA = {
  name: 'Bob',
  greeting: function() {
    alert('Hi! I\'m ' + name + '.');
  }
}
```
この状態で再度```personA.greeting()```を見てみます。
すると、アラートが```Hi! I'm Sam.```となるのが確認できたでしょうか。

つまり**thisを使わないとpersonA内のnameではなく、その外側の全体に対して定義されているnameが表示されてしまう**ということです。

もう少しわかりやすく言うと、**thisは今のスコープのオブジェクト自体を指します。**

今回はpersonAというスコープ内のnameを使いたかったので、thisが必要だったということですね。
そこまでわかると、以下のコードでなぜthisが入っていたかもわかるのではないでしょうか。

```js
function Person(name) {
  this.name = name;
  this.greeting = function() {
    alert('Hi! I\'m ' + this.name + '.');
  };
}

let personA = new Person('Bob');
let personB = new Person('Sarah');
```

# Allow関数を理解する

thisについてもう少し深堀りするついでに、Allow関数に関しても解説をしたいと思います。

まずはこちらのコードを見てください。

```js
var name = 'Sam';
var personA = {
  name: 'Bob',
  greeting: function() {
    console.log('Hi! I\'m ' + this.name + '.');
  },
  IntervalGreeting: function() {
    setInterval(this.greeting, 1000)
  }
}
```
アラートだと見てて鬱陶しいのでconsole.log()に変えました。これで1000ミリ秒ごとにコンソール上では繰り返し```Hi! I'm 〇〇.```と出力されるようになります。

*setInterval()というのはデフォルトで設定されている関数でsetInterval(関数名, 指定のミリ秒)と書くことで、指定したミリ秒ごとに関数が繰り返されるというものです。

そこで```personA.IntervalGreeting()```を実行して見ると、うまく表示されないのがわかるでしょうか。
```Hi! I'm Sam.```となってしまい、nameの部分がグローバル関数であるSamになってしまっていることがわかります。ただ、```personA.name```だと正しくnameはBobになっていますよね。

この違いは何でしょうか。

そこで重要になるのが、**thisは今のスコープのオブジェクト自体を指す**ということです。
personA.nameの場合、今のスコープのオブジェクトは当然personAになりますよね。
ただ、personA.IntervalGreeting()内のthis.greetingが表す今のスコープのオブジェクトはIntervalGreetingということになります。

このままでは少しわかりにくいかもしれませんが、setInterval()部分は以下のようにも書けるということを考えるとわかりやすいのではないでしょうか。

```js
var personA = {
  name: 'Bob',
  greeting: function() {
    console.log('Hi! I\'m ' + this.name + '.');
  },
  IntervalGreeting: function() {
    setInterval(
      function() {
        console.log('Hi! I\'m ' + this.name + '.');
      }, 1000
    )
  }
}
```
つまり、関数の中に関数が入っている入れ子状態なので、一番内側となる入れ子の中でthisをしたところで何を取得すれば良いのか、このコードでは判断がつかないということですね。

それではどうすれば良いのかというと、Allow関数を使えば解決します。
Allow関数を使う場合、このように書きます。

```js
() => {}
```

そしてAllow関数はthisを持ちません。つまり、**Allow関数を使った場合、一つ外側にthisを探しにいく(普通にスコープチェーンを辿る)**ということです。

なので、今回は下記のように書けば問題は解決します。

```js
var name = 'Sam';
var personA = {
  name: 'Bob',
  greeting: function() {
    console.log('Hi! I\'m ' + this.name + '.');
  },
  IntervalGreeting: function() {
    setInterval(() => { this.greeting() }, 1000)
  }
}
```

再度```personA.IntervalGreeting()```を実行してみると、グローバルで指定しているnameのSamではなく、正しく```Hi! I'm Bob.```と表示されることがわかりますね。

# まとめ

以上のことをまとめると以下のことになります。

* thisはオブジェクトを指すが、どのオブジェクトを指すかはどこで関数が呼ばれるかによって決定する
* Allow関数は自身でthisを持たない

Allow関数に関しては毎回使うべきというわけではなく、```メソッドでない場合には最適の方法```になります。(メソッドでは使わない方がいい場合が多い)

まだ少し混乱があるかもしれませんが、**もし迷ったら、とりあえずconsole.log()でログを出力してみる**ということも重要な方法になるので、実践しつつ感覚を掴んでいってもらえればよいかと思います。

もしもっと学びたいという方はぜひ[JavaScript道場](https://x-hack.connpass.com/event/196255/?utm_campaign=event_publish_to_series_watcher&utm_source=notifications&utm_medium=email&utm_content=detail_btn)にご参加ください。
次回は```canvasを使って背景やモンスターを描画する方法```を書いていきます。

それでは最後までお読みいただきありがとうございました。