Skip to content
uupaa edited this page Oct 24, 2015 · 30 revisions

このエントリでは、JavaScript コードのヘッダとボディを分離する理由について述べます。

コードはどんな時に読まれるか

あなたのコードはどんな時に読まれるのでしょう?

  • 80% の時間は「どんな機能があるか」を知るために使われます。
    • 「どんな API があるのか」「どんな引数で何を返すのか」を知るために何度も何度もチラチラと見られます。
  • 残り 20% の時間は「実装の確認のため」に読まれます。
    • デバッグする場合はこちらです。

あなたのコードは API の確認のために読まれています。

コードを読む人はどんな人か

また、あなたのコードを読む人はどんな人でしょう?

  • デバッグのために読む人。時間に余裕が無く、イライラしているかもしれません、恐らくは無関係な部分は読み飛ばしたいと考えています。またどこを見ればよいのか瞬時に知りたいと考えています。
  • 興味本位でコードを読む人。とりあえず上から下まで軽く読み流します。
  • 評価のためにコードを読む人。数秒〜数分でコードの質とコーディングセンスの良し/悪しも同時に読まれています。

アフタヌーンティー片手に、コードを読んでくれる人はかなり少なそうです。

あなたのコードは読む人の要求に答えているか

つまり、コードを読む人の殆どは「API を素早く確認したい」のです。

このような期待に応える術をもたない人や、そもそも読み手の行動をイメージできない人は、API を集中的に記載したヘッダ部分を作らず、実装(ボディ)にAPIの定義を混ぜ込んだ練度の低いごちゃごちゃとしたコードを書きます。

このようなコードは、時間当たりに得られる情報量が低いと考えられるため、多忙な人からは「あまり読みたくないコード」「汚いコード」と判断されてしまう事になります。

コードの読み手が、あなたの転職希望先の人事担当者 + 多忙を極める現場の偉い人(2〜3次面接の面接官)だった場合は、どうなるでしょうか?

どうすればよいか

最も良い方法は、コードの先頭部分に API の一覧を記載することです。
コードの先頭部分を一瞥すれば 何ができるか を判別できる状態にします。

これらは特別な新しいアイデアではなく、C/C++ のヘッダファイルの役割そのものです。

悪い例

こちらは、ヘッダ部分にも API の一覧がなく、またコードの追加でどんどん見通しが悪くなるため、読み手の時間を無駄に奪ってしまう悪い例です。
何をするにもスクロールが必要で、コードの追加に伴い見通しもどんどん悪くなる肥満体質(pyknic type)なコードと言えます。

(function(window, undefined) {
var MyExample = function() {

    ...

};

MyExample.prototype.get = function(arg1) {

    ...

};
MyExample.prototype.set = function(arg1) {

    ...

};
MyExample.prototype.clear = function(arg1) {

    ...

};
MyExample.prototype.reset = function(arg1) {

    ...

};

...

MyExample.prototype.move = function(arg1, arg2, arg3) {

    ...

};
MyExample.prototype.copy = function(arg1, arg2, arg3) {

    ...

};
MyExample.prototype.make = function(arg1, arg2, arg3) {

    ...

};
MyExample.prototype.remove = function(arg1, arg2) {

    ...

};

window.MyExample = MyExample;
})(window);

良い例

こちらは、コードの先頭部分(ファーストビュー)に API の一覧を集約したコードです。
殆どスクロールせずに、どんな機能が有るかを素早く把握できます。

関数の本体はヘッダの下に書きます。

GLOBAL["WebModule"]["exports"]("MyExample", function(global) {
"use strict";

// --- dependency modules ----------------------------------
// --- define / local variables ----------------------------
// --- class / interfaces ----------------------------------
function MyExample() {
    ...
}

MyExample["prototype"] = Object.create(MyExample, {
    "constructor": { "value": MyExample        }, // new MyExample():MyExample
    "get":         { "value": MyExample_get    }, // MyExample#get(arg1:Any):this
    "set":         { "value": MyExample_set    }, // MyExample#set(arg1:Any):this
    "clear":       { "value": MyExample_clear  }, // MyExample#clear(arg1:Any):this
    "reset":       { "value": MyExample_reset  }, // MyExample#reset(arg1:Any):this
      :
    "move":        { "value": MyExample_move   }, // MyExample#move(arg1:Any, arg2:Boolean, arg3:String):this
    "copy":        { "value": MyExample_copy   }, // MyExample#copy(arg1:Any, arg2:Boolean, arg3:String):this
    "make":        { "value": MyExample_make   }, // MyExample#make(arg1:Any, arg2:Boolean, arg3:String):this
    "remove":      { "value": MyExample_remove }  // MyExample#remove(arg1:Any, arg2:Number):this
});

// --- implements ------------------------------------------
function MyExample_get(arg1) {

    ...

}
function MyExample_set(arg1) {

    ...

}
function MyExample_clear(arg1) {

    ...

}
function MyExample_reset(arg1) {

    ...

}

...

function MyExample_move(arg1, arg2, arg3) {

    ...

}
function MyExample_copy(arg1, arg2, arg3) {

    ...

}
function MyExample_make(arg1, arg2, arg3) {

    ...

}
function MyExample_remove(arg1, arg2) {

    ...

}

return MyExample; // return entity
});