2010年5月8日土曜日

開発部のカメです。

はじめまして、開発部のカメです。
土曜日担当です。

三森さんと同じく物理をやっていました。
でも、院生のころに物理のセンスがない頃に(ようやく)気づき、
社会人になっても継続して勉強できる対象としてプログラミング言語 をやることにしました。

でいろいろあって、ご縁があって Sphia の仲間入り。

全然関係ないですが、 emacs 使いです。
Sphia では vi 派が多くちょっと肩身が狭いです。

せっかくですのでちょっと JavaScript のネタを一つ、クロージャに関することです。
関数を作ります、いわゆる AOP。条件は以下の通り。
・指定したオブジェクトのすべての関数の呼び出し前と後に、処理を追加したい。
・指定したオブジェクトのソースは変更したくない。
簡単に書くと以下のようになります。(FireFox向け)

var DynamicWrapper = function(_targetObj, _wrapperObj){
var wrapped = eval("(" + _targetObj.toSource() + ")"); // deep copy
for(var memberName in _targetObj){
if(typeof(_targetObj[memberName]) == "function"){
wrapped[memberName] = function(){
_wrapperObj.before(memberName);
var result = _targetObj[memberName].apply(_targetObj, arguments);
_wrapperObj.after(memberName);
return result;
};
}
}
return wrapped;
};

具体的な使用方法は、まず呼び出し前後に行う処理を定義したクラスを作成します。

var Wrapper = function(){
this.getDate = function(){
var date = new Date();
return date;
};
this.before = function(_functionName){
var date = this.getDate();
alert(date + ":" + _functionName + ":" + "start");
};
this.after = function(_functionName){
var date = this.getDate();
alert(date + ":" + _functionName + ":" + "end");
};
};

次に実際に処理するクラスを作成します。今回は簡単にsetter/getterを持った人クラスとします

var Person = function(){
this.name = "";
this.getName = function(){return this.name;};
this.setName = function(_name){this.name = _name;};
};

最後に Person クラスの関数 getName, setName の呼び出し前後に、Wapper クラスで定義した関数を呼び出させましょう

var wrapper = new Wrapper();
var person = new Person();
var wrappedPerson = new DynamicWrapper(person, wrapper);
wrappedPerson.setName(”カメ"); // 呼び出しの前後に、wrapper.before("setName")/wrapper.after("setName")が呼ばれる...
alert(wrappedPerson.getName()); // 呼び出しの前後に、wrapper.before("getName")/wrapper.after("getName")が呼ばれる...

実行するとなんか変ですね。
alertに表示される関数名が呼び出した関数名と異なる場合があると思います。
正確に書くと wrappedPerson.getName() を呼び出しても、 wrappedPerson.setName() を呼び出しても同じ関数が呼ばれます。
原因は DynamicWrapper クラスに存在し、クロージャを意識していないためです。
修正版の DynamicWrapper クラスは以下のようになります。


var DynamicWrapper = function(_targetObj, _wrapperObj){
var wrapped = eval("(" + _targetObj.toSource() + ")"); // deep copy
for(var memberName in _targetObj){
if(typeof(_targetObj[memberName]) == "function"){
wrapped[memberName] = (function(){ // 環境に束縛
var funcName = memberName;
var func = _targetObj[funcName];
return function(){
_wrapperObj.before(funcName);
var result = func.apply(_targetObj, arguments);
_wrapperObj.after(funcName);
return result;
};
})();
}
}
return wrapped;
};
長くなりましたので、解説は(要望があれば)次回に

0 件のコメント:

コメントを投稿