JavaScriptでクラス利用時にActionScript2の委譲クラス(Delegate)を移植した話
JavaScriptでクラス利用時などでスコープに手間取ったので、ActionScript2の委譲クラス(Delegate)を移植してみました。
クラス構造を使うと今までのActionScriptの資産も活かせるので積極的に使うようにはしているのですが、ここのところ、CreateJSでのイベント、enchant.jsのaddEventListenerなどでスコープの範囲で手間取るのでActionScript2の委譲クラス(Delegate)を移植してみました。
例えばこんなコード
フレームワークのコードで説明するとどうしても煩雑になってしまうので、今回はシンプルにボタンオブジェクトのonclickの例を挙げます。
こんなコード(Dogクラス)の実装があります。
ひとまずクラスを呼び出して使う
普通にクラスを呼び出してDogにwanwanさせてみます。まだボタンアクションはなく表示すぐに実行。
→別ページで開くクラスはこんな感じ。「わんわん!」をthis.messageで喋らせているのに注目ください。
// いぬクラス
var Dog = function (){};
Dog.prototype = {
message:"わんわん!",
wanwan: function() {
document.getElementById("debug").value = "this.message : " + this.message;
}
}
window.onload = function(){
// いぬクラスの呼び出し
_dog = new Dog();
// actionを実行すれば変数messageが表示。
_dog.wanwan(); // this.message : わんわん!
};
図に書くとこんな感じ。
ボタンで動くようにしてみる
では、ボタンを押して動くようにしてみます。コードはこんな感じ。
// いぬクラス
var Dog = function (){};
Dog.prototype = {
message:"わんわん!",
wanwan: function() {
document.getElementById("debug").value = "this.message : " + this.message;
}
}
window.onload = function(){
// いぬクラスの呼び出し
_dog = new Dog();
// ボタンのonclickイベントに関数actionを指定。
btnNormalElement = document.getElementById("btn");
// Dogクラスの変数messageが表示されるはずがundefined。
btnNormalElement.onclick = _dog.wanwan; // this.message : undefined
};
ActionScript3などで慣れていると、先ほどと単純に呼び出せした時と同じようになるのと思います。
予想図。
しかし、実際にはうまくいかない。
しかし、実際にはundefinedがでてthis.messageが参照できません。
→別ページで開くなぜ呼べないのか検証してみる
見た目上、問題無さそうなのに、なぜ呼べないのか検証してみます。試しにthisを表示してみる。
wanwan: function() {
// 試しにthisを表示してみる
document.getElementById("debug").value = "this : " + this;
}
と書いたのがコチラ。
→別ページで開くactionを押してみると「this : [object HTMLInputElement]」と表示されます。
さらにChromeのデバッグツールなどで細かく見てみると、このthisが示すオブジェクトはonclickで指定したボタンエレメントそのものです。
this.valueをしてみると・・・
ですので、this.valueをしてみると・・・。ボタンの名前が「action」と表示されます。いや、JavaScriptの挙動としては正しいのでしょうが、ちょっとどうにかしたい。
wanwan: function() {
// ボタンの名前が表示
document.getElementById("debug").value = "this.value : " + this.value;
}
→別ページで開く
そんなわけでDelegate登場
そんなわけで、ActionScript3のように、元のクラスの影響下がずっと続くスコープ感覚だと戸惑います。なるべく似たように書きたい。
ということで、私の場合は、このJavaScriptの挙動がActionScript2のそれに近かったので、以前のDelegateクラスを引っ張りだして以下のように実装しました。
// AS2で使っていた委譲クラスを移植
var Delegate = function (){};
Delegate.prototype = {
create : function ( __target, __func )
{
var f = function()
{
// f._targetから渡してもらった実行場所の参照
// 「この場所で実行されたことにしたいよ。」
var target = arguments.callee._target;
// f._funcから渡してもらった、動作対象の関数そのもの
var func = arguments.callee._func;
// Function.apply
return func.apply(target, null);
};
f._target = __target;
f._func = __func;
return f;
}
}
// いぬクラス
var Dog = function (){};
Dog.prototype = {
message:"わんわん!",
wanwan: function() {
document.getElementById("debug").value = "this.message : " + this.message;
}
}
window.onload = function(){
// Delegateクラス呼び出し
_delegate = new Delegate();
// 通常のいぬクラスの呼び出し
_dog = new Dog();
// ボタンのonclickイベントに関数actionを指定。
btnElement = document.getElementById("btn");
//
btnElement.onclick = _delegate.create( _dog , _dog.wanwan ); // this.message : わんわん!
};
Delegateの動作イメージはこのようになっています。
動作結果です。
→別ページで開くわんわん!と表示されました!ちゃんと、動きますね。
めでたしめでたし。
参考文献
文中では説明しきれなかったDelegateやスコープについての情報は以下に詳しく書いています。参考にしてみてください。
無気力関数 - Javascriptのスコープとdelegate
fladdict.net blog: JavaScriptでthisスコープをコントロールする
JavaScriptの再利用とapply | 勉強するのが、そんなに偉い訳!?
JavaScript のブロックスコープと名前空間 | Mozilla Developer Street (modest)
おわりに
いかがでしたでしょうか。
JavaScriptにおけるクラスとスコープについてはなかなか悩ましいですが、今回はDelegateという手法でまとめてみました。コーディングの一助となれば幸いです。
それでは、よきJavaScript Lifeを!





