JavaScript のクラスのメンバ関数をイベントハンドラに登録する方法
先週の土曜日 に JavaScript をいじり始めて真っ先に戸惑ったのが C++ と JavaScript の間の this の違いだった。JavaScript には「カレントオブジェクト」なる概念があって,this は「カレントオブジェクトへの参照」という定義らしい。で「カレントオブジェクト」そのものの定義はよく調べていない。そんなわけでグズグズなんだけど,いろいろ観察してみて分かったのは,どうやら
function clickHandler() { alert(this.id); }
な関数があって,
<span id="hoehoe" onclick="clickHandler()">ほえほえ</span> <span id="hoihoi" onclick="clickHandler()">ホイホイ</span>
で,呼び出されるとき「ほえほえ」をクリックしたときには<span id="hoehoe">(span ほえほえ)がカレントオブジェクトになり,clickHandler() の this は span ほえほえへの参照になる。で「ホイホイ」をクリックしたときは,this は今度は <span id="hoihoi">(span ホイホイ)への参照になっているらしいということ。この振る舞いは,JavaScript で作ったクラスのメンバ関数をイベントハンドラに登録(少なくとも,そういうコンセプトを実現)しようとすると問題になる。この前は
var ClassA = Class.create(); ClassA.prototype = { x : null, initialize : function(x) { this.x = x; : : // element が <span id="hoehoe"> を参照するように設定 : addEvent(element, "click", this.clickHandler); // element の onclick に this.clickHandler() を登録 }, clickHandler : function() { alert(this.x); } }; var a = new ClassA(10);
という風にイベントを登録したら,span ほえほえをクリックして ClassA::clickHandler() が呼び出されたとき,関数中の this が span ほえほえへの参照になっていて,「x というメンバはありません」というエラーになって困った。私が意図したのは をクリックしたときに,a.clickHandler() が呼ばれるような振る舞いだった。
これを実現するために私は「this が指す『カレントオブジェクト』は呼び出し時の文脈によって変わりうるから,それを文脈によって変わらないような呼び出し方にすればよい」と考えて対処法を考案してみた。
対策は以下の通り。ClassA::clickHandler() があったら,それに対応するグローバル関数
function classa_clickHandler(_this_) {_this_.clickHandler();}
を作って
addEvent(element, "click", this.clickHandler);
するところを
var _this_ = this; addEvent(element, "click", function() { classa_clickHandler(_this_); });
という風に変える。this を代入して作った _this_ は値なので,いつどのような形で呼ばれても,不変だろうというわけ。このアプローチはうまく動いた。しかし,「余計な関数を作るのは嫌だなぁ」とおもって微妙に違う次の形
var _this_ = this; addEvent(element, "click", function() { _this_.clickHandler(); });
を試したら,エラーになった。何でエラーになるのかは今のところ分からないんだけど。最初の書き方は _this_ が生き続けられるけど,次のはダメってことなんだろうな。
とりあえず余計な関数を一つ追加する(通常一行程度)だけで目的は達せられたので,まあいいかな。
まとめ
- JavaScript クラスのメンバ関数をイベントハンドラで呼ぶ方法について考察
- メンバ関数とは別に,_this_ を引数にとるようなグローバル関数を用意して,それをイベントハンドラに登録する方法で回避
- classa_clickHandler(_this_) 方式は OK だけど _this_.clickHandler() 方式は動かない。今のところ,xoinu には説明不可能。まあいいや(笑)
箇条書きでまとめてみました。