JavaScriptのクラス内でのアロー関数(thisの固定)の動作について理解しよう

JavaScriptのクラス内でのアロー関数(thisの固定)の動作について

JavaScriptにおいて、thisが参照するものは実行箇所によって異なります。

アロー関数を使うと、thisの実行箇所に関わらず参照先は変わらないので、コードが読みやすくなります。

アロー関数とthisの話はクラスに限った話ではありませんが、悩むことが多い概念なので、クラス構文における「this」の使い方としてまとめておきます。

構文

構文意味
() => {}アロー関数を定義する
アロー関数の仕様

サンプルコード

サンプルコードとして、クリック回数を計測するコードを紹介します。

NGコード

まずはNGコードを紹介します。

Output
NG例
Pug
table.table
			tbody
				tr
					th NG例
					td#targetTextNG
		button.btn.brandia_btn#targetButtonNG カウントボタン
JavaScript
const targetTextNG = document.getElementById( 'targetTextNG' );
const targetButtonNG = document.getElementById( 'targetButtonNG' );

//NGコード
class TargetLikeCounterNG {
  constructor() {
    // ボタンをクリックした数
    this.clickedCount = 0;

    const targetTextNG = document.getElementById( 'targetTextNG' );

    targetButtonNG.addEventListener('click', function() {
      this.clickedCount += 1;
      targetTextNG.textContent = this.clickedCount;
    });
  }
}

new TargetLikeCounterNG();

このコードはNGです。何故でしょうか?イベントリスナーの知識をきちんと持っていないと皆目検討もつかないでしょう。

イベントリスナー内のthisが参照するものはそのイベントターゲットになります。

つまり、上記のコードではthis.clickedCountTargetTextCounterのクラス変数を参照せず、イベントリスナーのthisを参照しようとします。

この場合はイベントリスナーで参照されるのはButton要素です。Buttonの属性にclickedCountはありませんから、上記のコードの様にButton属性のclickedCountに代入しようとしても弾かれます。

というわけで、ボタンを何回押しても「NaN」が返ります。

正しいコード

ただし、次の様にイベントリスナー部分をアロー関数に書き換えることで、プログラムは正しく動作します。

Output
正しい例
Pug
table.table
			tbody
				tr
					th 正しい例
					td#targetText
		button.btn.brandia_btn#targetButton カウントボタン
JavaScript
const targetText = document.getElementById( 'targetText' );
const targetButton = document.getElementById( 'targetButton' );

//コード
class TargetLikeCounter {
  constructor() {
    // ボタンをクリックした数
    this.clickedCount = 0;

    const targetText = document.getElementById( 'targetText' );
		
		targetButton.addEventListener('click', () => {
			this.clickedCount += 1;
      targetText.textContent = this.clickedCount;
		});
  }
}

new TargetLikeCounter();

アロー関数では、thisはアロー関数自身が定義された場所によって決まります。言い換えるなら、アロー関数は「イベントリスナー内であってもアロー関数自身が宣言された場所」によって決まります。

そのため、上記のコードではクラス内でアロー関数が定義されているのですから、この場合のthisはクラスになります。

結果、このthisはクラスのクラス変数「this.clickedCount」の「this」を正しく参照しますから、カウント計測ボタンは期待通りの動作をします。

この様にthisの扱いは要注意です。イベントリスナーのイベントターゲットを明示的に示す必要がある場合をのぞき、アロー関数の使用を統一した方がバグに悩まされることが少ないでしょう。

インストラクター