ほげほげー

C#メインにプログラミング周りから日常のあれこれとかを不定期に書いていきます。

JavaScriptコード規約プロトタイプとか

ノリと勢いだけでお仕事用にJavaScriptのコーディング規約を作ることになったので、
草案をまとめてみるとともに復習してみる。
(JSerではなくC#erなので誤りがある可能性は非常に高いです)

あまり固ーくしてもコーディングの楽しさが損なわれるし、
あまり意味もないので、ゆるふわなレベルにまとめてみる。
どちらかというと規約というよりガイドライン的な。
(考えなくてもいいことを増やす、的な)

参考にしたドキュメントやブログ。
サイボウズで学んだこと - IT戦記
Google JavaScript Style Guide 和訳
Felix's Node.js Style Guide

後はJavaScript: The Good Partsとか。

まずJavaScriptのコーディングスタイルで割ともめるところから。

セミコロンの有無

セミコロンはつけるべき。

本読んでると、JavaScriptセミコロンなしでも動くんだから、
できる限り無しで実装するべき、みたいなことが書かれていることがあります。

しかし、特定の条件下(しかも割と頻繁に発生する)で、
セミコロンが無いと非常にわかりにくい問題を生むため、
セミコロンは必ずつけるべき。

セミコロン | Google JavaScript Style Guide

意訳すると、問題が起こる場合には2つのケースがあり、
1つは、暗黙のセミコロンが意図せず機能しないことで発生するケース。
もう1つは、暗黙のセミコロンが意図せず機能してしまうことで発生するケース。
この二つの問題を避けるために、セミコロンは必須とするべき。

ステートメントの途中で改行する場合は必ず先行する行に演算子を書く

上記の暗黙のセミコロン問題のうち、意図せず機能してしまう事を防ぐために、
演算子は先行する行に記述するべきです。
人によっては可読性が落ちるという意見もあるかもしれませんが、
JavaScriptにおいてはそれはバグを生むため、厳密にこうするべきです。

var a = b ? c : d; // 1行で書けるのがベスト。
var a = b().c().d(); // 〃

// やむを得ず改行する場合

// OK
var a = b ?
        hogehogeChugahuga :
        hogehogeDhugahuga;

// OK
var a = hogehogeB().
        hogehogeC().
        hogehogeD();

// OK
var a = function() {
    b();
};

// NG
var a = b
        ? hogehogeChugahuga
        : hogehogeDhugahuga;

// NG
var a = hogehogeB()
        .hogehogeC()
        .hogehogeD();

// NG
var a = function()
{
    b();
};

特にC#er的にはピリオドでチェインする場合がとても気持ち悪いですが、
そこは仕方ないです。あきらめてください。

インデント

これは好みの問題だけど、無いとバージョン管理が割とめんどくさいので、 全員でスタイルをそろえた方が良い。 エディタの設定で簡単に統一できるしね。

というわけでここはふつーにはなして決めればいいや。 半角スペース2つか4つがベタかなあ。

命名規則

これも割と宗教論になりがちなので、 基本はJavaScnriptのデフォルトのものに従う。

で、見てたらこれがいい感じだったのでこれでいいや。 命名 | Google JavaScript Style Guide

  • functionNamesLikeThis
  • variableNamesLikeThis
  • privateNamesLikeThis_
  • opt_optionalArgumentNamesLikeThis
  • ClassNamesLikeThis
  • EnumNamesLikeThis
  • methodNamesLikeThis
  • CONSTANT_VALUES_LIKE_THIS
  • foo.namespaceNamesLikeThis.bar
  • filenameslikethis.js

配列やらオブジェクトやらはリテラルを使う

new Object(); とか new Array(); みたいに書けますが、
めんどくさかったり、バグを生んだり冗長だったりとイマイチなので、リテラルを使いましょうというお話。

var obj1 = {};
var obj2 = {
        prop1: 1,
        prop2: 2
    };

var arr1 = [];
var arr2 = [1, 2];

変数宣言時はvarを必ず付ける

JavaScriptではvarをつけずに変数宣言をした場合、
グローバルな空間に定義されてしまうので、必ずvarで変数宣言をする。

function sample(opt_poyo) {
    var hogehoge = "hogehoge"; // OK
    hugahuga =  "hugahuga"; // NG
    opt_poyo || opt_poyo = "poyopoyo"; // 引数は関数内にスコープが限定されるのでOK
}

利用するライブラリの選択

弊社の場合はjQuery一択。
デザイナーがいないから最もデザイナーに普及してて使われているjQueryを選択せざるを得ない。

他にもClosureとか色々あるけど、デザインは外部に任せるからSHOGANAI。
いろんなライブラリ使うともろもろの問題が起きるようなので。 参考: サイボウズで学んだこと - IT戦記

関数定義はfunction文ではなく代入文で実装する。

// function文
function foo() {
  /* 何か */
}

とするのではなくて、

// 代入文
var foo = function () {
  /* 何か */
};

とする。

それぞれの特徴は以下の通り。

function文

1.コンパイル時に静的に定義されるため順序が前後しても良い 2.文が完結するため、閉じる際にセミコロンが不要

代入文

1.prototypeで実装する時には必ず利用することになる。 2.即時関数として利用可能なため、誤動作を防止するために閉じる際、セミコロンが必要

それぞれ細かい所で微妙に違うのがJavaScriptの面白いところですね。
で、function文の場合、静的に定義されるという点が曲者で、
(人によっては感じ方が異なるとは思いますが)
具体的には下記のように記述されていても動いてしまいます。

hoge(); // 実行できる

function hoge() {
  alert("hoge");
}

hoge(); // 実行できる

もう少し厳密に言うと、定義されたスコープ内に静的に定義される、
ということになるため、スコープ外からの参照は出来ません。

で、代入式の場合どうなるかというと

hoge(); // hoge is not defined

var hoge = function () {
  alert("hoge");
};

hoge(); // 実行できるが、上記のErrorで止まる

となります。
後方参照性が無いって言う感じですね。
代入文の実行時に関数が定義される事になるためです。

それで、何故代入文を推奨したいかというと、

prototypeに関数を定義する時には必ず利用することになる。

ためです。
両方意識するよりも片方だけ意識した方が考えること減るよね。
程度のことですが、できることが(ほとんど)変わらないのであれば、
こうしていくのが良いと考えます。

JSお行儀ガイド

他の言語は多少わかるけどJSわかんないんです><;
的な人向けのJSにおけるお行儀についてまとめる項。
主にC#er向け。 ちょうぷろとたいぷ。
クロージャもりもり使ってくの。 (当方C#erなので正直正しいかびめう)

名前空間

これ大事。使わないと簡単にグローバルな名前空間を汚染しちゃうので。

C#には当然のように備わっている機能ですが、
JSには標準で備わっていないため、少し工夫が必要です。
とはいえ、難しいことは何もなく、オブジェクトを名前空間代わりに使ってしまおうということです。 後述のモジュールと被りますが、まあ名前空間の概念はこんなもんだよ、ってことで。

var nameSpace = {};
nameSpace.createMethod = function () { /* なにか */ };

こうするとグローバルな名前空間を汚さずに済む、
つまり、ほかのライブラリとの競合を防ぐことができるので、
絶対に忘れないようにしましょう。

複数人で手を加える場合は何らかの規則を設ける必要もありますね。
たとえばファイル名と名前空間を一致させるとかなんとか。
ここは厳密にしないと後で痛い目を見そう。
いろんな場所で同じ名前空間定義してあばばばするとか。

クラス宣言

これも他の言語には備わっている機能ですが、
JavaScriptはprototypeベースの言語なので、クラスベースなOOPへのサポート具合はイマイチ。
(中途半端に対応した感がパないの) ではどうするか。
クロージャと関数オブジェクトなどなど、JSの機能をもりもり使って作りましょう。

var SampleClass = function () {
    function SampleClass() {
        this.privateVariable = "private"; // プライベート変数を定義
    }

    // プライベートなメソッドを定義
    function privateMethod() {
        alert("call private method");
    };

    // パブリックなメソッドを定義
    SampleClass.prototype.publicMethod = function () {
        alert(this.privateVariable);
    };

    return SampleClass;
}();

var sampleClass = new SampleClass();
sampleClass.publicMethod(); // プライベート変数の値がアラートで表示
sampleClass.privateMethod(); // error

けっこーめんどくさいですが、こうすることで名前空間を汚さず、隠ぺいもできるので。

モジュール宣言

割とめんどくさいです。 詳しくはサンプルを。

var SampleModule;
(function(exported) {
    var privateVariable = "private";

    exported.publicMethod = function () {/* ほげほげー */};

    var ClassSample = (function(){/* 上記みたいな */}());
    exported.ClassSample = ClassSample;
}(SampleModule || SampleModule = {}));

上記の名前空間と併用して拡張していけばいい感じにグローバルな空間を汚染せずに整理できるはず。

おまけ:TypeScript

上記のクラス宣言とモジュール宣言の所は完全にTypeScriptのコンパイル結果からパクりました。
参考にしたもの:TypeScript Playground

Playgroudがすごく楽しいので軽くでもいいので触ってみるべき。
コンパイル後のJavaScript見れば何となくJavaScriptのいい感じな書き方がわかるし一石二鳥。
(自分の知ってる限りの知識では、概ね、というかJavaScriptとしてGood!な感じで出力される)

いいわーこれいいわー。TypeScriptの記述と、JS変換後の記述がさほど変わらないのもイイ。
静的型付なのでコード補完もほぼ完ぺきだし。
ドキュメント(英語)見る限りちょこっといじるだけで、
jQueryもサクサク補完してくれる環境作れるっぽいし。
パないの!
まぁ、まだ全然触ってないから地雷踏んでないってのもありそうだけど。

ジェネリックにも0.9.0から対応したっぽいし、
これはこれからに期待できるのではないでしょーか?
というかこれ使いたい。

VisualStudioのプラグインがあるってのも素敵(MS製だしね)
ということで使いたくなってきたので、今度英語ドキュメント読んでまとめる。

とりあえずこんな感じで、足りなさそうなことがあればまた随時追加する形で。


2013/08/20 一部リンク修正
2013/08/21 ちょっと追加