JavaScriptにおける"this"
2007.01.21 / javascript
仕事で最近マジメにJavaScriptでOOPな開発をしています。
言わずもがなJavaScriptはオブジェクト指向な言語ではありますが、JavaやC#のようなクラスベースなオブジェクト指向ではなく、プロトタイプベースなオブジェクト指向言語です。
このあたりの話は自分の手でコードを書いたことはなくとも、理屈では分かっていた「つもり」だったので「ほうほう」と適当に流していたのですが、いざ自分で書いてみると相当ハマるハメになりました。 ちょっとまだ理解は完璧ではないのですが、備忘録としてここに記しておきたいと思います。
こんなことをしたい、と仮定します。
・./foo.phpにGETでリクエストを投げる
・レスポンスを受け終わるとshowComplete関数をコールバック
・「Complete!!」と表示する
まーよくありがちな流れです。
僕も何度もこんなコードは書いたことはありましたが、ちょっとオブジェクト指向を意識して書いた途端、まったく動作しなくなってしまいました・・・。
さて、次のコードがその動かないコードなのですが、どこが動かないかすぐに分かりますか?
ちなみに要prototype.jsです。
ダメな点は2つあります。
まず、
ではなく、
と、なります。
変数urlはFooのプロパティとして存在しているのでグローバル参照じゃダメなのですね。
(JavaScriptの場合、グローバル参照はwindow.
このテのコードを書くときはいままでurlなんかはグローバルに定義していたのでハマることはなかったのですが、OOPで実装した途端、まずここでハマりました。
でも、まだダメなんですね。
showCompleteの呼び出し方なのですが、先ほどと同じようにthis.showCompleteとしてもダメです。
なぜなら、このコールバックの呼び出し元はイベント発生源であるAjaxオブジェクトであるので(*)、ここでのthisはAjaxオブジェクトを指しています。当然のごとくshowCompleteなんて関数は定義されていないのでここでエラーが出るなり、コールバックが失敗に終わって延々と何も起こらない状態が続いてしまいます。
(*)ここが肝なのですが、JavaScriptの場合(と、いうかECMAScript?)「this」のスコープは関数を呼び出したオブジェクトに設定される仕様になっています。なので、闇雲の「this」参照すると、ツボにハマることになります。
じゃぁ、どうするかというと、ここでのthisのスコープをズラす必要があります。
イベント登録時にクラスFooの定義時に使用しているthisの参照を指定してあげることで、想定通りのthisが渡ります。prototype.jsではこのちょっと面倒な概念をシンプルにしてくれるbind関数が用意されてあるので、それを使ってあげるとこんな形で書けます。
これで想定したとおりの、つまり、FooオブジェクトのshowComplete関数が呼び出されます。
(コレは相当ハマった)
まとめるとこんな感じになります。
JavaScriptはなかなか奥が深いです。。。クラスベースの言語に慣れているとこの「thisのスコープ移動」にはなかなか理解するのに苦労しました。 とは言え、JavaScriptもまだまだ書く機会は相当多い言語ではあるので、もっと理解を進めたいと思いますよ!