JavaScriptのthisではまったのでメモしておきます。
はじめに
JavaなどのようなOOPに慣れ親しんでいると、JavaScriptのようなプロトタイプベースの言語を触ると痛い目を見ますね。あまりにも自由度が高すぎるためです。オブジェクトとそのメンバは密接に結びついているという概念を一度忘れ去る必要があるみたいです。
thisを使った例
<html> <head> <title>thisのテスト</title> <script type="text/javascript"> var someObj = { name : 'SomeObject', whoAmI : function() { alert('I am ' + this.name + '.') } } function init() { var button1 = document.getElementById('button1') var button2 = document.getElementById('button2') button1.onclick = someObj.whoAmI button2.onclick = function(){ someObj.whoAmI() } } </script> </head> <body onLoad="init()"> <button id="button1" name="ぼたん1">押してね1</button><br> <button id="button2" name="ぼたん2">押してね2</button><br> </body> </html>
実行結果
「押してね1」ボタンをクリックすると「I am ぼたん1.」というアラートが、
「押してね2」ボタンをクリックすると「I am SomeObj.」というアラートが出ます。
イベントハンドラに関数オブジェクトを代入する
button1.onclick = someObj.whoAmI
この代入はsomeObjというオブジェクトのwhoAmIというフィールドに設定されていた関数オブジェクトをbutton1のonclickイベントハンドラに設定することを意味します。
つまり、ボタンがクリックされた際のオーナー(呼び出し主)はボタン1オブジェクト自身となるため、nameで参照できるのは「ぼたん1」となるわけです。
イベントハンドラにクロージャを代入する
button2.onclick = function(){ someObj.whoAmI() }
そしてこちらは、function(){ ... } という形で無名関数(≒クロージャ)を代入している。
この場合、クリックされた際はオーナーであるボタン2オブジェクトがクロージャを呼び出し、クロージャ内部の処理で、someObjをオーナーとしてのwhoAmIというフィールドに設定されている関数オブジェクトが呼び出されます。オーナーがsomeObjなので、「SomeObj」が出力されるという仕組み。
キーポイント
大事なのは、
function() { alert('I am ' + this.name + '.') }
という関数オブジェクトは、「その時のオーナー(this)のnameというフィールドを使ってアラートする」という処理が行われるということです。
関数オブジェクトは誰のものでもなく、今回たまたま宣言された時にsomeObjの上に乗っかっていたと考えると分かりやすいかもしれません*1。誰のものでもないので、動的に付けたり外したりできるわけですね。*2
2009-05-02追記
トラックバックでid:SiroKuroさんよりご指摘いただいた、部分を修正しました。
Javaでは…thisが参照するインスタンス「の持つメソッド」は固定的にできるわけですね。
参考
JavaScriptのthisキーワードをちゃんと理解する - builder
JavaScriptのthisキーワードをちゃんと理解する - builder
JavaScriptのthisキーワードをちゃんと理解する - builder
JavaScript の this について - IT戦記