2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

アロー関数とthisの解説

Last updated at Posted at 2020-03-28

アロー関数とは?

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

サンプルプログラム

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しているからと考えられる。1

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

例1

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

例2

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

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

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

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

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

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

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
var f2 = obj.f;
f2()

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

bindとは?

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

setTimeout(f, 0);

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

次のようにすれば bind を自作できる2

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

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

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

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
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に上書きする。

  1. setTimeout関数やaddEventlister関数はthisを書き変えることで有名。

  2. MDN Web Docsにある bind のポリフィルは new や引数にも対応している。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?