引き続き JavaScript の話

たとえ型仙人とはいえ

なんかといっちゃあすぐクラス作っちゃって1クラス1JSとかしてJSファイルをむやみやたらに増やしまくってサーバ管理の方々に大ひんしゅくな私ですけど、恐縮です。とはいえ、関数が一級オブジェクトであることのすごさみたいなものをギラギラに顕す何かこう、僕なんかからみると生ものっぽいすごいコードを日々見せつけられていると、さすがの型仙人たる自分もミクロな局面では「ここはクロージャでがんばってみようかな…」と思ったりするわけで、すごくたいしたことない話なんですけど、今日はこんなの書きました。

var getSuccession = function(/*Function*/finalize)/*Function*/ {
    var /*Number*/length = 0; // いわゆる private なインスタンス変数初期化
    return function()/*Function*/ {
        length++; // インクリメント
        return function() {
            if ( --length == 0 ) { // デクリメントして限界値きたら終了ハンドラ実行
                finalize();
            }
        };
    };
}

えへへ、教科書的なクロージャの使い方。いつ使うかというと、ASだとアニメーション、JSだとXHRっていうような非同期な処理を一束にして並列で走らせて、誰が最初で誰が最後に終わるかわかんないけど、とりあえず全員終わったらこれやってね。っていう時。よくあるよくある。ちなみに今日は、ユーザさんが入力したデータをサーバ側で validate する処理を元々同期で(synchronousに)書いていたものが並列で複数走らせることになり、ありゃ弱ったな。ってなったのがきっかけ。でもこういうのが自分の脳から出てきたところを自分で自分を褒めてあげたい。

普段だったら絶対 Flex のアニメーションにあるような(あったっけな)、Parallel クラスを書くかどっかから持ってくる訳ですけど、

var parallel = new Parallel();
parallel.addEventListener(Parallel.end, this, this.onParallelEnd);
parallel.push(function(){//...});
parallel.push(function(){//...});
parallel.start();

こういうやつ。でも今日はクロージャで書いてみよーってことで書いた。

// 終了ハンドラを設定しつつ関数をもらう
var /*Function*/ success
    = getSuccession(
        function(){
            alert('ok');
        }
    )
;
var /*Function*/ showError
    = function(/*Object*/rawData) {
        alert('dame');
    }
;
var /*Array*/validators = $A([]);
validators.push(
    this.getServerValidatorName(name)
    .valid(success()) // 実行してカウンタをインクリメントしつつハンドラ関数を渡す
    .invalid(showError)
);
validators.push(
    this.getServerValidatorYear(year)
    .valid(success()) // 実行してカウンタをインクリメントしつつハンドラ関数を渡す
    .invalid(showError)
);
validators.invoke('validate'); // せーのどん

もちろん、ネタだけ一緒で今新たに書きました。ほら「いったい何個並列に処理が走るのか?」ってのと、「一個一個終わったら数を数えて全員に達したら終了処理実行!」ってのを一緒にやりじゃないですか。ちなみに invoke とかは prototype.js ね。
きっとプログラムを専門に高等教育で学んだ方々の間には、こう言うのには必ず定石があるんだろうけど、プログラムというものをちゃんと勉強してないのがこういうとき重くのしかかってきますね。

ファクトリーだって関数で

ほんで上の getホニャララServerValidator ってやつはファクトリーを関数でやってみて、valid だったときと invalid だったときのハンドラをそんままチェーンして設定できるようにしてみたりした形。JSっぽいぽい。

var getServerValidator = function(/*Object*/parameters)/*Object*/ {
    var handle = {};
    handle.valid = function(/*Function*/validHandle)/*Object*/ {
        handle.validHandle = validHandle;
        return handle;
    };
    handle.invalid = function(/*Function*/invalidHandle)/*Object*/ {
        handle.invalidHandle = invalidHandle;
        return handle;
    };
    handle.validate = function()/*Object*/ {
        Ajax.Request(
            'validate.py'
            {
                method     : 'post',
                parameters : parameters,
                onSuccess  : handle.validHandle.bind(this),
                onFailure  : handle.invalidHandle.bind(this),
            )
        ;
        return handle;
    };

    if ( !arguments.callee.handles )
        arguments.callee.handles = [];

    arguments.callee.handles.push(handle);

    return handle;
};

var getServerValidatorName = function(/*String*/name)/*Object*/ {
    return getServerValidator({name:name});
};

var getServerValidatorYear = function(/*String*/year)/*Object*/ {
    return getServerValidator({year:year});
};

いまビール飲みながら思い出しつつ書き起こしてるので、上のは実際にゃ動かなそうですけど、こういう感じでファクトリー(ぽい)ことだってクロージャ使いまくりの関数で書けちゃうぜっていう。継承っぽいこともできちゃうぜっていう。関数すげえなおい。っていうかでもよく見ると上のは perl っぽいな、bless かっていう。handle ってのが隠蔽されてて、bless されてそいつがオブジェクトとして大活躍!。みたいなイメージでしょうか。これ handle っていう中間の Object インスタンスに頼らない方がもっとかっこいいね。

というわけで、こないだみたいにカタカタしたのもできるし、こういうハイブリッドなのもできるし、JavaScript 面白いです!