アロー関数とthisの解説

# アロー関数とは？

アロー関数の`()=>{}`は`function (){}.bind(this)`と同じ。ただし、functionのように関数内部にオブジェクトを指すthisを生成しない。

## サンプルプログラム

```javascript:JavaScript
function outer(ff) {
  setTimeout(ff, 0);
}
class T {
  f() {
    var obj = {
      ff: () => { console.log(this); }
    };
    outer(obj.ff);
  }
}
var t = new T;
t.f();
```

obj.ffはアロー関数なので関数内部にオブジェクトobjを指すthisを生成しない。
obj.ffはアロー関数なのでfunction(){}.bind(this)と同じ。
関数obj.ffにbindするthisはオブジェクトTになる。
このプログラムはTを出力する。

obj.ffを外側の関数outerに移動させてもTを出力するのは**アロー関数がthisをbindしている**からと考えられる。[^setTimeout]


# functionが関数内部にオブジェクトを指すthisを生成するとは？

例１

```javascript:JavaScript
var obj = {
  f: function () { console.log(this); },
  g: () => { console.log(this); }
}
obj.f();
obj.g();
```

例２

```javascript:JavaScript
function Obj() { }
Obj.prototype.f = function () { console.log(this); }
Obj.prototype.g = () => { console.log(this); }
obj = new Obj;
obj.f();
obj.g();
```

例１も例２も通常関数fは関数内部にオブジェクトobjを指すthisを生成し、thisがobjを指す。
アロー関数gは関数内部にオブジェクトobjを指すthisを生成しないので、thisがWindowを指す。

obj を指す。
アロー関数 g は関数内部にオブジェクト obj を指す this を生成しないので、this が window を指す。

# function が関数内部にwindowオブジェクトを指す this を生成しない特殊なケース

```javascript:JavaScript
class P {
  a() {
    return function () {
      console.log(this); // undefined
    };
  }
}
new P().a()();
```

クラス内でfunctionをreturnしたときだけ**なぜかthisがundefinedになる**。

```javascript:JavaScript
function Q() { }
Q.prototype.a = function () {
  return function () {
    console.log(this); // window
  };
};
new Q().a()();
```

prototypeでオブジェクトを作成したときはいつも通りthisがwindowになる。

## function関数とthis

function関数の内部にオブジェクトを指すthisを生成する**関数の呼び出し形式**は、次の4通りが判明している。

- `obj.method()`の形式でfunction関数を呼んだ場合のmethod内
- `obj["method"]()`の形式でfunction関数を呼んだ場合のmethod内
- `new Class`の形式で呼んだの場合のconstructor内
- `new function`の形式で呼んだ場合のfunction内

これら以外の形式で読んだ場合、function関数の内部にあるthisはオブジェクトではなくWindowを指してしまう。

たとえば

```javascript:JavaScript
var f2 = obj.f;
f2()
```

としたら、`f2()`は`obj.f()`の形式で関数を呼んでいないので、例１も例２も関数内部のthisがオブジェクトobjではなくWindowになってしまう。:confounded:

# bindとは？

```javascript:JavaScript
var f = function () {
  console.log(this);
}.bind({ a: 1 })

setTimeout(f, 0);
```

bindは関数内部のthisをオブジェクト{a:1}で束縛し固定する。
このプログラムはthisとしてオブジェクト{a:1}を出力する。

次のようにすれば bind を自作できる[^mdn_bind]。

```javascript
Function.prototype.mybind = function(obj) {
  var _this = this;
  return function(...args) {
    return _this.apply(obj, args);
  };
};
```

bind が束縛するとは、クロージャーを作って function 関数内の this がオブジェクト obj を指すようにすることだとわかる。


# addEventListenerとsetTimeoutが上書きするthisは何か

```javascript:JavaScript
event_target.addEventListener('click', function (e) {
  console.log(this === e.currentTarget); // true
  console.log(this === event_target); // true
});
```

`event_target.addEventListener(type, listener)`があったとき、addEventListener関数は`listener.apply(event_target)`をして、listener関数内のthisをevent_targetに上書きする。
thisとevent_targetとe.currentTargetは同じ値になる。

```javascript:JavaScript
class T {
  fun() {
    console.log(this); // Tを返す
    setTimeout(function () {
      console.log(this); // Windowを返す
    }, 0);
    setTimeout(()=> {
      console.log(this); // Tを返す。setTimeoutがapplyするthisではなく、アロー関数がbindするthisが有効になる。
    }, 0);
  }
}
new T().fun();
```

`setTimeout(timeout_fun, delay)`があったとき、setTimeout関数は`timeout_fun.apply(window)`をして、timeout_fun関数内のthisをwindowに上書きする。

[^setTimeout]: setTimeout関数やaddEventlister関数はthisを書き変えることで有名。

[^mdn_bind]: [MDN Web Docs](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Function/bind)にある bind のポリフィルは new や引数にも対応している。