連絡先 Hikwareホーム
Hikware.Tech
自分用の覚え書きをそのまま公開。参考程度にどうぞ。

イベントリスナー内の this が何物なのか分かるまで

公開日 2018/02/04
最終更新 2018/02/04
  • C++ や Java 星人にとって JavaScript の this は混乱しまくるのだが、それを理解できるまでにお世話になったサイト

最初さっぱり意味がわからんくて困惑

 JavaScript の勉強したての頃にこんなコードを書いて、大層困惑したのですよ。

class MyClass
{
    private mouseCount: number = 0;

    constructor()
    {
        // イベントリスナーとして自分のメソッドを渡す
        document.addEventListener( 'mousedown', this.onMouseDown );    
    }

    onMouseDown(
        e: MouseEvent)
    {
        // この this は当然自分の事と思いきや、違う
        // この例の場合だと、this は document を指してる
        this.mouseCount++;
    }
}

 まあよく考えれば、addEventListener に渡した物は単なる関数なので、確かにこのコードだけでは this が紐づいてなさそうな雰囲気は察したのだけど、エラーにもならず onMouseDown はちゃんと呼ばれて、でも thisdocument ってのがさっぱり意味が分からない。

 で、「JavaScript this」 とかで調べると、まーみんな苦労したのか山ほど解説が見つかるのだけれど、以下でやっとしっくりきた。

 JavaScript において this ってのは関数の持ち主の事なので、イベントリスナーは登録先(今回の場合は document)が保持して呼んでる以上、関数内の this はそのオブジェクトになる、と?
 いやよく読むともっと面倒な事になってる気もするけど、まあなんにしろイベントリスナー内で this をそのまま使っちゃいけないという事は分かった。

で、どうすりゃいいのよ

 んでじゃあ、皆どうやって this を渡してるのかと調べてみると、昔は、

// this を一度ローカル変数に渡して、それを無名関数に渡す
var self = this;
document.addEventListener( 'mousedown',
    function(e) {
        self.なんやらかんやら;
    });

とかいろいろ工夫してなんとかしてたらしいですが、最近制定された アロー関数(C#のラムダ式と同じ記法)で記述した場合は、関数内の this は関数定義した場所と同じ this を指す という事になったらしい。単なる function() の略記法かと思ってたら、超有能な子だった。

 てことで、アロー関数を使って最初の例を書き換えると以下になりますね。結局どうやってもクラスメソッドをそのままイベントリスナーにはできないみたいだけど、まあこれならいいか。むしろ下手にメソッドにするよりその場で書けって事ですね。

document.addEventListener( 'mousedown', (e) => { this.onMouseDown(e); } );    

 なお、アロー関数はES6機能なので、IE は 11 でも使えないみたいですが、僕は最初から TypeScript 経由でしか JavaScript を使う気がないので、気にせず使いますよ。コンパイル結果を見てみたら、上の self を使った例みたいに展開されてました。