JavaScriptのコールバックで クラスthis が参照できないの解決策

JavaScriptの this  ってオブジェクト指向と相性が悪い・・・

最近JSでクラス(class)を使っていて、そう感じる場面がありました。

そこでJavaScriptでクラスを使う場合、
コールバック内でクラス(class)のthisを参照する方法 をまとめておきます。

これは気を付けてないと重大なバグの温床になるかもしれません(怖)

問題に遭遇したのはクラスを作っていた時・・・

昔のJavaScriptだと クラス なんて高尚なものは使えなかったです。

せいぜい prototype  で疑似的なクラスを再現する程度みたいな感じ

でもES2016から class  が正式にサポートされてます。これは大きな進展

▼ あと各ブラウザでの対応状況

モダンブラウザでは確実に使えるうえ、モバイルでも対応してるブラウザは多いです。

 

それで困ったのはクラス内でコールバックを使った時

なんとコールバック内で this  を呼び出すと エラー になってしまいました。

▼ その時のコードを簡略化したもの

▼ そうするとこんなエラーが・・・

this.doAfterLoaded is not a function、、、

ちゃんと this  を付けて呼び出してるし、ましてや未定義なんてこともありません。

実を言うと、僕はこんな思い込みしてしまってました。

クラス内なら this は無条件でクラスのものになるはず

残念ながらそうならないのが JavaScript の厄介さ

他のオブジェクト指向言語になれるとこんな風に思いがちなんですが、JavaScriptの this  はオブジェクト思考専用のキーワードじゃなかったんです。

JavaScriptの this は文脈で全然別物になる

注意しないといけないのは次の点

JavaScriptでの this はクラス専用のキーワードじゃないこと

まあJSに慣れてる人ならこれは当然の認識かもしれないですけどね。

コンテクスト(文脈)によって全く別のオブジェクトになります。当然クラス内で呼び出したからと言って、無条件にクラス自身のインスタンスを指すことにはなりません。

 

たとえば addEventListener  のコールバック内の場合・・・

その中での this  は要素への参照を指すことになるって説明されてます。

▼ リファレンスでの解説

addEventListener() を使って要素にハンドラー関数を設定したとき、ハンドラーの中の this の値は要素への参照となります。これはハンドラーに渡された event 引数の currentTarget プロパティの値と同じです。

引用元 : https://developer.mozilla.org/ja/docs/Web/API/EventTarget/addEventListener

この説明の "ハンドラー" とは "コールバック" のこと

この説明の通り、 addEventListener  内での this  はイベントの起きた要素を指すらしいです。

 

あと addEventListener  を使ってない場合、
this  が指し示すコンテキストは window  を指すという点にも注意が必要ですね。

▼ どういうことかと言うと、こういうこと

こういう風にJavaScriptでは this  がクラス専用のキーワードという "常識" は通用しません。

クラス内コールバックで class の this を参照する方法

ではどうすれば class  の this  を参照できるのか・・・

手軽な解決策としては次の2つ

  • コールバックに bindメソッド で指定方法
  • コールバックに アロー関数 を渡す方法

それぞれをコード例で示すなら、次のようにして参照可能です。

1.コールバックにbindメソッドで明示的に指定する方法

まず1つめの方法は・・・

bindメソッドから this のコンテクストを明示指定すること

▼ MDNでのbindメソッドについての解説

bind() メソッドは新しい関数を生成し、その関数が呼び出された時の一連の引数の前に、提供された値が設定された this キーワードが追加されて呼び出されます。

引用元 : Function.prototype.bind() - JavaScript | MDN

コールバックは関数なので、直接 bind にクラスの this  を渡せば解決という訳です。

 

実際にクラスの this  をコールバック内で使うコード例は次の通り

▼ こんなコード

ハイライトした6行目に注目!

ここでコールバックに対して bindメソッドから this  を指定してます。

▼ 今度はちゃんとアラートが表示された

コールバック内でthisを明示的に指定したら、クラス内メソッドが期待通りに呼び出せた

コールバックに直接 渡せるので、コード的にもこっちの方が分かりやすいかも

他の情報源だと apply  とか call  も使えるらしいですが、自分はコチラの方法を使ってます。

2.コールバックとしてアロー関数を渡す方法

2つめはコールバックにアロー関数を使う方法

▼ アロー関数とは何なのかを3行で

アロー関数式は、より短く記述できる、通常の function 式の代替構文です。
また、this, arguments, super, new.target を束縛しません。
アロー関数式は、メソッドでない関数に最適で、コンストラクタとして使うことはできません。

引用元 : アロー関数 - JavaScript | MDN

この説明だと メソッドでない関数 = コールバック で使うのが最適と読み取れます。

もちろん bindメソッド でも似たことはできるんですが、
アロー関数の方がもっともっとスマートにコードが書けるのが利点です。

 

たとえば addEventListener  の場合、次のように書けばOK

▼ こんなコード

先ほどの説明の通りアロー関数は this  を束縛しません。

だから this  を渡さなくても、そのままクラスの this  が取得できるってことです。

もちろん同じことは jQuery の onメソッド でも可能

 

ちなみにアロー関数の対応状況は Can i use を参照のこと

▼ アロー関数の各ブラウザの対応状況

残念ながら(?)IEでは一切使えない模様・・・

でもモダンブラウザや主要なモバイルブラウザなら使えるので問題なし。

(でも "なぜか" 日本だと IEのシェア率は 10%以上 あるのが厄介。もう今2019年だよ)
(もういい加減 IEシェアは 1% 切ってくれた方がいい気がする・・・)

ここまでのまとめ

コールバックでクラス this  を参照する方法をまとめておくと・・・

  1. コールバックにbindメソッドで明示指定する方法
  2. コールバックとしてアロー関数を渡す方法

どちらを使っても同じですが、古いブラウザを気にしないなら2番目ですね。

重大なバグを残さないように明示指定するように気を付けたいです。ではではまた