JavaScriptのthisとは？

こちらは、 [CAMエンジニア Advent Calendar 2019](https://qiita.com/advent-calendar/2019/cam-inc) 12日目の記事です。
昨日は [maruken24](https://qiita.com/maruken24) さんの [レンダリングパターンをいらすとやで解説してみた](https://qiita.com/maruken24/items/71461c6a0247bbc9d4e5) でした。

こんにちわ！KeitaroArataです。
Qiita初投稿させていただきます。

今年の春に新卒フロントエンジニアとして入社させていただき、
Web未経験ですがサービスに携わらせて頂いております。
駄文ですが、最後まで読んでいただき、ご指摘などいただけると嬉しいです。

## 概要
半年ほど前に`thisが変わる問題`みたいなことを先輩が言っていたことを思い出しました。
どうやら、メソッドの中でthisを使うと、想定とは違った挙動をすることがあるようです。
今回はその問題に対して目を向けていこうと思います。

## thisで呼んでいるのに…
```js
this.__nickname = "kebosu"
const object = {
  console: function() {
    console.log(this.__nickname);//undefined
  }
}
object.console();
```
上記のようなコードなんですが、みなさんこちらって何が帰ってくると思いますか…？
僕はなんとなくで書いたんですけど、これって`undefined`が返ってくるんですよ。不思議ですね。
直感的には`kebosu`が返ってくる事が期待されるこのコード。
さて、この問題を解決し、`kebosu`を出力させるには一体どうしたら良いのでしょうか。
まずは、この出力しているthisは一体何者なのかを調べる必要がありそうです。

`console.log(this);`をsetTimeout内のfunctionに入れてみると…
`Object {console: function(...)}`と、objectの中身が出力されました。
おや？ということは
理想：グローバルオブジェクトを参照
現実：オブジェクトの中身を参照
となっているというわけですね。
さて、ここでthisという曖昧なものをはっきりとさせましょう

## thisとは
`this`とは呼び出される場所によって姿を変える独特な変数のことです。
1. 関数内のthisは，グローバルオブジェクトのままである
2. メソッド内のthisは，そのメソッドが属するオブジェクトを指す
3. コンストラクタから呼び出したthisは，そのコンストラクタが生成したオブジェクトを指す

大体のthisが上記の三パターンに分類できるそうです。
(ちなみに僕ここで初めて知ったんですが、関数とメソッドって別物なんですね…)
12/12追記：別物というか、関数の一部をメソッドと呼ぶ感じですね。

さらにthisを操作する方法として、`bind`というものがあります。
`bind`とは、thisの参照先を変更する事ができる関数です。

```js

this.__nickname = "kebosu"
const object = {
  console: function() {
    console.log(this.__nickname);//kebosu
  }.bind(this)
}
object.console();
```
と、bindしてあげることで、無事`kebosu`が出力されましたね。
thisの参照先を指定してあげることで問題は解決しました。
万事解決と言いたいところですが、上記のコードだった場合は、わざわざbindしてあげる必要もないのです。

```js

this.__nickname = "kebosu"
const object = {
  console: () => {//arrow function
    console.log(this.__nickname);//kebosu
  }
}
object.console();
```
このように、アロー関数を使ってあげれば良いのです。

## アロー関数
ざっくりいうと関数式(関数リテラル)をより簡潔にかける記法のことです。
アロー関数というのは具体的には下記のようなものです。

```js

//es6
const hoge = () => {
  console.log("hoge")
}

hoge();//"hoge"が出力;
```
これはes6から使えるようになった記法です。
ちなみにコレをes5に変換すると↓

```js

//es5
var hoge = function hoge() {
  console.log("hoge");
};

hoge();//"hoge"が出力;
```
見比べてみると、アロー関数の方が簡潔に記述されている印象があり、スッキリした書き方ですね。
引数に`function`が入るもの（`map`、`foreach`等）にもこの記法は有効です。
という、上記程度の知識でしかアロー関数式というものを認識していませんでした。
この機会にMDNを見てみると
>アロー関数式は、より短く記述できる、通常の function 式の代替構文

最初に言っていた、簡潔な書き方というのは正しいようですね。安心しました。
しかし、さらに読み進めて行くと…

>アロー関数自身は this を持ちません。レキシカルスコープの this 値を使います。

レキシカルスコープというワードが出てきましたね。
次にレキシカルスコープの話をしましょう。

## レキシカルスコープ
まず、ローカル変数の有効範囲を決める概念としてレキシカルスコープとダイナミックスコープというものがあります。
・レキシカルスコープ...関数を定義した時点でスコープが決まる
・ダイナミックスコープ...関数を呼び出した時点でスコープが決まる

言語ごとにスコープの仕様は決まっているようで、JavaScriptはレキシカルスコープです。
前章の話と繋げると、アロー関数式は関数を定義した時点でのthisを用いるようです。
つまりは__アロー関数式を定義した際のスコープでthisが決まる__ということですね。

```js

const object = {
  num: 1,
  fnA: function fnA(){console.log(this.num)},
  fnB: () => {console.log(this.num)}
}

object.fnA();//1
object.fnB();//undefined
```
こちらを見ていただけるとわかりやすいですが、従来のfunctionとアロー関数で挙動が違うことがわかると思います。
・function: 内部のthisは実行時のレシーバであるオブジェクトになる
・アロー関数: 内部のthisは宣言時のスコープを持つオブジェクトになる

今回の例だと、`object`がレシーバオブジェクトであり、functionでは`object`内のnumがthisの参照先になっているということですね。
一方、アロー関数は参照がグローバル(window)オブジェクトになっているため、windowオブジェクトのnumを参照しようとしていますが、定義されていないため`undefined`が返ってきています。
そのため、varで変数を用意してあげると↓

```js

const object = {
  num: 1,
  fnA: function fnA(){console.log(this.num)},
  fnB: () => {console.log(this.num)}
}
var num = 2;
object.fnA();//1
object.fnB();//2
```
のような結果になります。
this、奥がふかいですね。

## まとめ
・thisとは呼び出した場所によって中身が変わる変数
・アロー関数とはes6から生まれた、関数宣言を簡潔にかける方法である。
・jsはレキシカルスコープである。
・アロー関数はthisの参照元が宣言時にbindされる

## 最後に
今回、thisについてお話ししましたが、setTimeoutと絡めてあげたらどんな挙動をするのかだったり、クロージャの話など、展開できる話題は多くあります。
今後も機会があれば記事にまとめて、あとで見返せるような形で知識を広げていきたいです。
今回は、初の記事ということで、稚拙な文章を見せてしまったかもしれないですが、ここまで読んでいただき誠にありがとうございました。

明日は [@kita21](https://qiita.com/kita21) の記事になります
お楽しみに！

## 参考

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions/Arrow_functions
https://developer.mozilla.org/ja/docs/Web/JavaScript/Closures
https://qiita.com/shoichiimamura/items/aec874fc5cf55cde7fa1
https://qiita.com/mejileben/items/69e5facdb60781927929
https://qiita.com/t-tonchim/items/07d763325c7cb659efb7
https://wemo.tech/904
http://enlosph.hatenablog.com/entry/2017/01/20/222605