【JS学習その⑦】JavaScriptにおけるthis

#JS学習シリーズの目的
このシリーズは、私ジャックが学んだJavaScriptのメカニズムについてアウトプットも兼ねて、
皆さんと知識や理解を共有するためのものです。
(理解に間違いがあればご指摘いただけると幸いです)

#thisとは
「**呼び出し元のオブジェクトへの参照を保持するキーワード**」

```main.js
const person = {
    name: 'Tom',
    hello: function() {
        console.log('Hello ' + this.name);
    }
}

person.hello(); /*Hello Tom*/
```
上記のコードでは、

1.JavaScriptではまず変数`person`を呼び出す
2.`person`内の`hello`メソッドに参照が向く
3.`hello`メソッドが参照している`function`を実行する

↑の流れになっています。
**`this`**はこの時、呼び出し元のオブジェクトである**`person`**を参照するので、`this.name`は`person`オブジェクトの`name`プロパティを参照するというわけです。

#参照のコピーとthis
thisの基礎が分かったところで、次のようなコードを見てみましょう

```main.js
window.name = 'John';

const person = {
    name: 'Tom',
    hello: function() {
        console.log('Hello ' + this.name);
    }
}

const ref = person.hello;
ref(); /*Hello John*/

person.hello(); /*Hello Tom*/
```

変数`ref`に`person.hello`を代入した場合、変数`person`内にある`hello`メソッドへの参照をコピーします。
この状態で`ref()`を実行すると、変数`person`を呼び出していないので、thisは呼び出し元のオブジェクトが`person`ではなくなります。
この時、thisがどのオブジェクトを参照するのかというと、**グローバルオブジェクト(windowオブジェクト)**を参照します。

ここで非常に重要なことを言いますが、

**オブジェクトのメソッド**として実行される場合
'this' => **呼び出し元のオブジェクト**

**関数**として実行される場合
'this' => **グローバルオブジェクト**

↑のようにthisの参照先は変わります。

したがって

```main.js
window.name = John;

function a() {
    console.log('Hello ' + this.name);
}

a(); /*Hello John*/
```
上記のようなコードでは、関数`a`内の`this`はグローバルオブジェクトを参照するので、`a()`を実行した結果は、`'Hello John'`となります。

#コールバック関数とthis
オブジェクトのメソッドをコールバック関数として実行した場合を見てみます

```main.js
window.name = 'John';

const person = {
    name: 'Tom',
    hello: function() {
        console.log('Hello ' + this.name);
    }
}
person.hello(); /*Hello Tom*/

function fn(ref) {
    ref();
}

fn(person.hello); /*Hello John*/
```
上記のコードのように、
`person.hello()`を実行した場合は、今までどおり`this`は`person`を呼び出し元のオブジェクトとして参照します。
しかし、
関数`fn`にコールバック関数として`person.hello`を渡した場合、`person`オブジェクト内の`hello`の参照先である`function`を変数(引数)に代入しているのと同じことなので、
コールバック関数`ref()`は`person.hello`を参照先とする関数として実行され、`this`はグローバルオブジェクトを参照します。

#bindとthis
関数として実行するけど、thisの参照先を呼び出し元のオブジェクトにしたい。という時は、'bind'というメソッドを使います。

```main.js
window.name = 'John';

const person = {
    name: 'Tom',
    hello: function() {
        console.log('Hello ' + this.name);
    }
}
person.hello(); /*Hello Tom*/

const helloTom = person.hello.bind(person);

function fn(ref) {
    ref();
}

fn(helloTom); /*Hello Tom*/
```
上記のコードのように、
変数`helloTom`に`person.hello.bind(person)`と書くと、`bind`メソッドによって`person.hello`の`this`の参照先が`person`オブジェクトに固定されます。
したがって、コールバック関数として`fn(helloTom)`を実行すると、`'Hello Tom'`と出力されます。

このように、`bind`メソッドによる`this`の参照先の固定を**bindによるthisの束縛**と表現します。

また、'bind'は'this'の参照先だけではなく、そのメソッドまたは関数の**'引数'**も固定できます。

```main.js
window.name = 'John';

function a(name) {
    console.log('hello ' + name);
}

const b = a.bind(null, 'Tim');

b(); /*hello Tim*/
```
上記のように書いた場合、`bind`の第2引数以降に指定したキーワードで関数の引数を固定できます。(※引数の固定は、複数指定できます)
※`bind`の第１引数で`this`の参照先を固定しない場合は、`null`を指定します。

ここで、重要なメカニズムとして`bind`メソッドでは、(今回の例で説明すると)

1.`person.hello`の参照先である`function`が存在する
2.`person.hello.bind(person)`によって`this`の参照先を`person`に固定した`function'`が別のメモリ空間にコピーされる

この、**'this'の参照先を固定した別のfunction'がメモリ空間に作成される**というところは大事なのでしっかり理解しておきましょう！

##call,applyと'this'
前述した'bind'のもう一つの特徴として**'bind'の使用時点で実行はしない**という点があります。

ここで紹介する'call'メソッドと'apply'メソッドは、**使用時点で実行します**。

```main.js
function a(name, name1) {
    console.log('hello ' + this.name + '' + name + ' ' + name1);
}

const tim = {name: 'Tim'};

const b = a.bind(tim, 'Bob', 'John');

b(); /*hello Tim Bob John*/

a.apply(tim, ['Bob', 'John']); /*hello Tim Bob John*/
a.call(tim, 'Bob', 'John'); /*hello Tim Bob John*/
```

上記のコードのように、
`bind`では使用時点では実行されず、`b()`のように関数を実行した時点で実行されますが、
`call`,`apply`では、使用時点で実行されます。
`call`と`apply`の違いは、固定する引数の指定をする際に、`call`は`bind`と同じように**「,」(カンマ)区切り**で指定しますが、`apply`は**配列**で指定します。

#アロー関数と'this'
今まで、'this'について解説してきました。ここでアロー関数と'this'について解説します。
アロー関数とは、「**無名関数を記述しやすくした省略記法**」です。
そして結論から言いますが、
「**アロー関数内では、'this'という値を保持しません**」
では、アロー関数内で'this'を使ったらどうなるか？次のコードを見てみましょう。

```main.js
window.name = 'John';

const person = {
    name: 'Tom';
    hello: () => {
        console.log('Hello ' + this.name);
    }
}
person.hello(); /*Hello John*/
```
上記のコードでは、
`person.hello`メソッドではアロー関数を使用しています。そして、そのメソッド内で`this`という値を使用しています。
前述した通り、アロー関数では'this'という値を保持しません。そこで、**'this'はスコープチェーンをたどってレキシカルスコープ(外部スコープ)から値を探します**。
今回の場合、直近の値はグローバルオブジェクト(windowオブジェクト)内の**window.name**になります。
よって`person.hello()`の値は`'Hello John'`となります。

#まとめ
いかがでしたでしょうか。
JavaScriptにおいて'this'は初学者が躓きやすいところだと思いますが、しっかり理解しておきましょう！

#おまけ
今回の解説では、
オブジェクト内のメソッドを下記のように書きましたが、

```main.js
const person = {
    name: 'Tom',
    hello: function() {
        console.log('Hello ' + this.name);
    }
}

person.hello(); /*Hello Tom*/
```
下記のように省略した記法もあり、主にこちらの書き方で書かれます。この書き方にも慣れておきましょう。

```main.js
const person = {
    name: 'Tom',
    hello() {
        console.log('Hello ' + this.name);
    }
}

person.hello(); /*Hello Tom*/
```