2010年6月5日土曜日

7. 恐怖の仮面this

<pre> <next>
myHandlerに渡される引数のeはイベントが発生した位置などを含む情報が入っています。しかし、サンプルコード2のaddEventListenerに渡されるthis.myHandlerには引数がありません。さらに、試しに、サンプルコード1のfunction(e) { self.myHandler(e) を function() { self.myHandler()とするとエラーになります。これでも良いような気もするのですが、どうもSafariの実装が
怪しいような気も・・・(笑)

いずれにしても、最初の二行は同じですし、動作も同じです。その後も変数が一カ所違うだけだと思って見逃すと・・・
探偵失格です(大笑)

それでは事件解明にかかりましょう。

犯人はthisなのです。二つのサンプルにあるthisは別人…いや、別のオブジェクトを指しています。

addEventListenerの使い方として良く紹介されているのですが、サンプルコード2のthisはHTMLの<div>を指しています。JavaScriptの仕様では、イベント駆動の元になるオブジェクトがJavaScriptのオブジェクトではなく、HTMLの<div>であると考えているようです。

ところがサンプルコード1のthisではMyObjectから作られたmyRectオブジェクトを指しています。たとえば、サンプルコード1では以下のようになっています。
alert(this.myObject.offsetLeft);
これはthis.myObjectで表す四角の左端の座標offsetLeftを表しています。ではthis.myObjectとは?と言うと、MyObject()内にある以下のコードで以下のように表しています。
this.myObject = myObject;
そう、例の問題のコードです。この左辺のthisがmyRectになるわけです。だからこそサンプルコード1では以下の式でmyRectのプロパティが呼び出せたのです。逆に、サンプルコード2ではHTMLの<div>にはthisというプロパティがないので「undefined」になってしまったわけです。
alert(this.myVar);
サンプルコード2ではHTMLの<table>などHTMLの入れ子構造(ネスト構造)になっているものを扱うには分かりやすく便利そうです。しかし、上記のように親となった関数を指したくなる場合がある事も事実です。

実際にWebKitのサンプルコードsticky-notesでは、addEventListenerで呼び出されたハンドラから、さらにaddEventListenerでハンドラを登録してそれぞれで変数を共有しています。グローバル変数という手もありますが、オブジェクトが繰り返し生成される(四角が作られる)たびにグローバル変数を生成していては後始末が大変です。

個人的には明示的にどちらを選択するのかを引数を追加するか、関数名で明示したほうが分かりやすいように思うのですが・・・。

閑話休題、一件落着したと思ったのですが、まだ謎が残っていました。それは・・・つづく(笑)
<pre> <next>

0 件のコメント: