@ledsun blog

無味の味は佳境に入らざればすなわち知れず

JavaScript殺法 11のリファクタリング

f:id:ledsun:20130124000038j:plainJavaScriptを書いていてぶち殺したくなった時によく使うリファクタリングです。

1.定義順を整理

JavaScriptパターンの5.4.1 モジュールパターンの開示を参考に、var、処理、API公開の順に並べなおす。

function () {
    //宣言
    var hoge = 'hoge',
    fuga = '';

    //処理
    fuga = foge;

    //APIの公開
    return {
        hoge: hoge,
        fuga: fuga
    };
}

2.戻り値をオブジェクトにする

戻り値を増やしたいときにまずオブジェクトに変えてから、値を増やす。
型付けが弱い言語は二つ以上の値を返すのが当たり前なのが凄い。

function () {

    return {
        hoge: hoge,
        fuga: fuga
    };
}

3.戻り値をオブジェクトでなくす

「戻り値をオブジェクトにする」の逆。
戻り値を一つしか返さないならプリミティブ型の方が分かりやすい。

function () {

    return hoge;
}

4.匿名関数に名前を付ける

何をするかを名前で表せるのは大きい。
30行超えてたら一回しか使わなくても有名関数として定義した方が分かりやすい

$button.click(function () {
    //なにかする
});

var clickFunc = function () {
    //なにかする
};

$button.click(clickFunc);

5.関数の抽出

処理を関数に抽出。

var ary = [1, 2, 3],
ret;
for(var i = 0; i < ary.length; i++){
    ret += ary[i]1;
} 

var ary = [1, 2, 3],
ret,
sum = function (ary) {
    var ret;
    for(var i = 0; i < ary.length; i++){
        ret += ary[i];
    }
    return ret;
};

ret = sum(ary); 

使用している変数を引数やローカル変数にし忘れることが多いので注意。JSLintやJSHintを使えば容易に発見できる。

6.DOM参照を変数に格納

リファクタリングかチューニングか微妙だけど、まあよくやります。セレクタもDRYじゃないと嫌。

if ($('.list').val() === '') {
    $('.list').val() = 'abc';
}

var $lists = $('.list');

if ($list.val() === '') {
    $list.val() = 'abc';
}

7.変数と関数を組み合わせる

引数を減らしたり、外部から変更できなくしたいときに使う

var hoge = 'hoge',
return window.alert(hoge);

alertHoge = function () {
    var hoge = 'hoge',
    return window.alert(hoge);
}

alertHoge();

alertHoge = window.alert.bind('hoge');

alertHoge();

8.thisを引数に加える

callやapplyは見づらい。イベントハンドラに使う関数はthis参照が必要なので注意。

var func = function () {
    $(this).val('abc');
}

func.call(this);

var func = function (self) {
    $(self).val('abc');
}

func(this);

9.即時関数をbindやcallに書き換える

function () {
    window.alert(this);
}();

window.alert.bind(this)();

window.alert.call(this);

10.イベントの抽象化

イベントハンドラでカスタムイベントを投げて、監視するイベントを集約。

<div class="component">
  <input type="button">
  <input type="textbox">
</div>
$(.component [type='button']).on('click', function () { window.alert('happy!'); };
$(.component [type='textbox']).on('blur', function () { window.alert('happy!'); };

var $compo = $('.component'),
$compo.find([type='button']).on('click', function () { $(this).parent.trriger('happy'); });
$compo.find([type='textbox'].on('blur', function () { $(this).parent.trriger('happy'); });

$compo.on('happy', function () { window.alert('happy!'); };

11.$.dataにDOM参照を入れておく

子オブジェクトの処理を子オブジェクトのメソッドっぽく定義したいときに使う。
実装を知らないと利用できないので、良いリファクタリングかは微妙

window.alert ($(this).find('.child'));

function () {
    var $child = $.find('.child');

    $child.func = function () { window.alert ($child) };
    $.data(this, 'child') = $child';
}()

$.data(this, 'child').func();