イベントリスナーに関数を登録

 
Web Tips.
Booskanium's
Booskanium's Web Tips.

JavaScriptのイベントリスナー設定の関数名でかっこ無し、HTMLのイベント属性への設定は関数名かっこ有りと暗記していませんか。

target.addEventListener(type, func [, options]);
検索しても、この理由を解説しているコンテンツが見つかりませんでした。

本題ですが、落書き人がお勧めな書き方は

target.addEventListener(type, function(e){ obj.func(e); } [, options]);
です。
この書き方に落ち着いた理由は、後述のとおりです。これはここの落書き人が「this」の参照先が???になり気付かされた事です。

イベントリスナーに登録する関数

先に言っときますが、かなり回りくどく、専門用語で逃げている説明になってしまいました。自分で読み返しても難解です、落書き人のメモですのでこれで善しとします。

まずは、イベントリスナーへの登録構文は下記のとおりです。

target.addEventListener(type, listener[, options]);

この構文の「listener」については仕様で「指定された型のイベントが発生するときに通知 (Event インターフェースに準拠しているオブジェクト) を受け取るオブジェクト。」とMDNで解説されています。

上記を解釈から、巷に溢れている例で「関数名をかっこ無しで書く」は

target.addEventListener(type, func [, options]);
関数変数名の中身(関数オブジェクト)を代入(コピー)するという事になります。
ちなみに、ここで「func()」と書くと、イベントリスナー登録時に、func()が即実行され、その戻り値がイベントリスナーに設定されることになり、あれれとなります。トリッキー記述大好きな人なら、可読性最悪にする手段として使えます。

つまり巷に溢れている例では、イベントが発生した時に代入(コピー)させた箇所であるdocumentオブジェクトをthisが参照しています。
【重要】これで困るのは、 関数記述中でthisを用いていたときです。オブジェクトリテラル中に記述されていると関数でthisを使っている場合に、イベントリスナー中にコピーされた関数は、そのオブジェクトリテラルが示すthisではありません。

※つまりファンクションリテラル指定がベター
※関数リテラル中に書いたイベントリスナーではthisが拘束されるアロー関数で登録するのがベターです。

そこで落書き人は、オブジェクトリテラルの変数がobjで、そのリテラル中に記述されている関数がfuncなら以下の様に記述しています。
target.addEventListener(type, function(e){ obj.func(e); } [, options]);
上記で関数の引数にeと書いているのは、イベント発生したオブジェクトを明示的に引き渡す事をはっきりさせています。
なおこの方法なら下記の様に、複数の関数を順番に処理させることも出来ます。
target.addEventListener(type, function(e){ obj.func1(e);  obj.func2(e);} [, options]);

上記を確認するコードです

オブジェクトリテラルの中に書いてある関数をリスナーに設定した時に、そのオブジェクトリテラルの中でthisが何を示すかで、使い勝手が違います。
下記はそれを確認するコードです。

var vari1 = "グローバル"; 
var setJob {
	vari1: "へのへのもへじ",
	func1: function() {
		consol.log(this.vari1);
	}
}
window.addEventListener('load',setJob.func1; },false);

この記述ではsetJobの中のfunc1を呼んだのに、"グローバルがコンソールに出力されます。
window.addEventListenerにセットされている関数の中身は、リスナー登録した時の、setJob.func1の中身がコピーされたものが実行されるからです。
では次は下記のように書いてみましょう。

var vari1 = "グローバル"; 
var setJob {
	vari1: "へのへのもへじ",
	func1: function() {
		consol.log(this.vari1);
	}
}
window.addEventListener('load',function(){ setJob.func1(); },false);

今度は"へのへのもへじ"がコンソールに出力されます。
なぜかといえば、後者はsetJobの中のfunc1が呼び出されるからです。
この説明でチンプンカンプンだ。という人は、もう一度最初から理解しながら読みななおしてください。
これもJavaScriptのthisの罠のひとつです。良く言えば柔軟性が高い、悪く言えばJavaScriptを難解に感じてしまう。

実は、この仕様の理解の妨げになるのが、デベロッパーツールのデバッカーです。イベントリスナーに関数記述が代入されているのに、デバッカーは関数の記述元で動いているかのように見せてくれます。このJavaScriptのデバッカーの見せ方を理解していなかった落書き人には、これがなんとも難解にみえました。