性能計測機能を作ってみた3

前回作成したコンストラクタ版を、クロージャを利用したモジュール版に変更した。

背景

JavaScriptでは、コンストラクタと一般の関数との区別がない。
そのため、コンストラクタとして使用されることを想定して実装した関数でも、もちろん new 演算子なしに呼び出されることが文法的にはあり得る。
new 演算子なしに呼び出された場合、関数内の this はグローバルオブジェクトとなるため、新規作成したオブジェクトのフィールドとして格納される想定であった変数がグローバル領域に格納されてしまう。
このような事態を避けるために、始めから new 演算子で呼び出されることを想定せず、単なる関数呼び出しをされた際の戻り値として新規作成したオブジェクトを返すつくりとした。

var st = speedTester();
st.setStart('処理1');
// 処理1のコード
st.setEnd('処理1');

st.outputAllResults();
/**
 * 性能計測機能を提供します。
 */
function speedTester() {
    var start = {};
    var end = {};

    /**
     * デフォルトメッセージを取得します。
     * @param result 計測結果オブジェクト
     */
    function _getDefaultOutputString(result) {
        return '[debug]' + result.id +': ' + result.milliTime + '(ms)';
    }

    /**
     * 複数結果用のデフォルトメッセージを取得します。
     * @param result 計測結果オブジェクトリスト
     */
    function _getDefaultOutputStringForAll(resultList) {
        var str = '';
        for(var i = 0, len = resultList.length; i < len; i++) {
            var result = resultList[i];
            str += _getDefaultOutputString(result);
            if(i + 1 < len) {
                str += '\n';
            }
        }
        return str;
    }

    /**
     * デフォルト出力先にメッセージを出力します。
     * @param str メッセージ
     */
    function _outputToDefault(str) {
        if(console.log) {
            console.log(str);
        }
        else {
            alert(str);
        }
    }

    return {

        /**
         * 性能計測を開始します。
         * @param id 計測を一意に識別するための文字列。同時に複数の計測を行わない場合は省略可能
         */
        setStart: function(id) {
            start[id] = new Date().getTime();
        },

        /**
         * 性能計測を終了します。
         * @param id 計測を一意に識別するための文字列。同時に複数の計測を行わない場合は省略可能
         */
        setEnd: function(id) {
            end[id] = new Date().getTime();
        },

        /**
         * 性能計測結果を出力します。
         * @param id 計測を一意に識別するための文字列。同時に複数の計測を行わない場合は省略可能
         * @param outputFunc(result) 計測結果出力用関数。引数は、{id:計測id文字列, milliTime: 計測結果数値(ミリ秒)}。
         *                           省略した場合は、デフォルト出力先にデフォルトメッセージを出力
         */
        outputResult: function(id, outputFunc) {
            var milliTime = end[id] - start[id];
            var result = {id: id, milliTime: milliTime};
            if(outputFunc) {
                outputFunc(result);
            }
            else {
                var str = _getDefaultOutputString(result);
                _outputToDefault(str);
            }
        },

        /**
         * すべての性能計測結果を出力します。
         * @param outputAllResultsFunc(result) 計測結果出力用関数。引数は、{id:計測id文字列, milliTime: 計測結果数値(ミリ秒)}のリスト。
         *                                     省略した場合は、デフォルト出力先にデフォルトメッセージを出力
         */
        outputAllResults: function(outputAllFunc) {
            var resultList = [];
            for(var id in end) {
                var milliTime = end[id] - start[id];
                resultList.push({id: id, milliTime: milliTime});
            }
            if(outputAllFunc) {
                outputAllFunc(resultList);
            }
            else {
                var str = _getDefaultOutputStringForAll(resultList);
                _outputToDefault(str);
            }
        },

        /**
         * すべての計測結果を消去します。
         */
        clearAllResults: function() {
            start = {};
            end = {};
        }
    };
}

参考文献

JavaScript: The Good Parts ―「良いパーツ」によるベストプラクティス

JavaScript: The Good Parts ―「良いパーツ」によるベストプラクティス