JavaScript style guide

 

はじめに

このガイドでは mozilla/browser 内のコードを対象としています (すなわち Firefox)。

既存のスクリプトを編集する際には、既存のコードに合わせるためにこれらのガイドラインをいじらなければならないかもしれません。すでに大量の変更を加えているのであれば既存のコードの書式を見直すことも検討すべきですが。

これは Neil のガイド を元にしていますが、コードレビュー担当者は最新のスタイルに合わせるためにこれを更新してください。

ホワイトスペース

  • 基本的なインデントはスペース 2 つです。タブを使ってはいけません。
  • 1 行は 80 文字以下になるようにしてください。行を折り返すときは前の行の関係のあるアイテムと並ぶようにインデントしてください。インデントの例 をご覧ください。
  • 行末にスペースを入れないようにしてください。たとえ 2 項演算子やコンマやセミコロンの後ろでもだめです。
  • 2 項演算子はスペースで離してください。
  • コンマやセミコロンの後ろにスペースを入れてください。前にではありません。
  • キーワードの後ろにスペースを入れてください。例:if (x > 0)
  • 定義のブロックの間には空行を 1 つ(もしくは 2 つ)入れてください。大きなコードのブロックも空行で分割するようにしてください。
  • ファイルの最後には改行してください。(特に emacs ユーザに当てはまります。)

記号

  • インラインの関数やオブジェクトでは、コンマやセミコロンの前を除いて波括弧の前後にスペースを入れてください。
function valueObject(aValue) { return { value: aValue }; }
  • パラメータのリストや配列の添え字など、角括弧の中ではスペースは必須ではありません。これは丸括弧の中のインラインオブジェクトを折り返す場合や、for (;;) 構文も該当します。後者に関して、最初のセミコロンの後ろに通常必要なスペースは 2 番目のセミコロンによっていらなくなり、2 番目のセミコロンの後ろのスペースは閉じ括弧によっていらなくなります。
  • 二重引用符を使うようにしてください。インラインのイベントハンドラ内や二重引用符をくくる場合は例外です。
  • 波括弧はその親の文に対してインデントしないでください。既存のファイルではそこで使われているスタイルを一貫して使ってください。ただし、新しくファイルを作るときは以下の使用可能な構文から好きなものを選んで使っても構いません。
if (dlmgrWindow)
  dlmgrWindow.focus();

if (dlmgrWindow) {
  dlmgrWindow.focus();
} else {
  dlmgr.open(window, null);
}
  • 非 ASCII 文字に関しては \uXXXX 形式の Unicode エスケープ を使用してください。XUL、DTD、スクリプト、プロパティファイル向けの文字セットは UTF-8 です。

コードスタイル

  • else は上で示したように常に単独の行に書いてください。
  • return の後には else を使わないでください。
if (x < y)
  return -1;
if (x > y)
  return 1;
return 0;
  • i++++i も使用可能です。
  • インライン関数はそのデバッグが楽になるように名前を付けてください。関数をプロパティに代入しただけではその関数に名前を付けることはできません。次のようにしてください。
var offlineObserver = {
  observe: function OO_observe(aSubject, aTopic, aState) {
    if (aTopic == "network:offline-status-changed")
      setOfflineUI(aState == "offline");
  }
};

関数や変数のネーミング

  • 名前や列挙値には interCaps のような形式を使用してください。他の定数は UPPER_CASE のようにしてください。
  • コンストラクタは interCaps を使用し、1 文字目は大文字にしてください。
  • インタフェース名用の利便のための定数の名前は nsI から始まるようにしてください。
const nsISupports = Components.interfaces.nsISupports;
const nsIWBN = Components.interfaces.nsIWebBrowserNavigation;
  • 列挙値は k という文字から始まるようにしてください。例:const kDisplayModeNormal = 0;
  • グローバル変数は g という文字から始まるようにしてください。例:gFormatToolbar
  • 引数(パラメータ名)は a という文字から始まるようにしてください。
  • プライベートメンバは _ から始まるようにしてください。例:_internalFunction
  • イベントハンドラ関数は on という語から始まるようにしてください。具体的には onLoadonDialogAcceptonDialogCancel のような名前を使うようにしてください。こうすることで明白になります。
  • 関数名、ローカル変数、オブジェクトのメンバに接頭辞はいりません。
  • ローカル変数はできるかぎりそれが使用されるところの近くで宣言するようにしてください。そしてすべての変数を初期化するようにしてください。
  • XUL オーバレイでコードを挿入するときは、既存あるいは将来の関数名や変数名との衝突を避けるために、関数や変数を一意の名前を持つオブジェクトの中に隠蔽してください。
var UniqueName = {
  _privateMember: 3,
  publicMember: "A string",

  init: function UN_init() {
    this.doSomething(this.anotherMember);
  },

  doSomething: function UN_doSomething(aParam) {
    alert(aParam);
  }
};

JavaScript らしい部分

  • 以下のような厳格な JavaScript の警告が書いたコードに対して出ていないかを確認してください。
    • 変数の宣言の重複
    • return;return value; の混合。
    • JavaScript オブジェクトの宣言における末尾のコンマ。
    • 未宣言の変数やメンバ。配列の値が存在するか不確かな場合はインデックスを配列の長さと比較してください。オブジェクトのメンバが存在するか不確かな場合は "name" in aObject を使用してください。期待している特定の型があるのであれば typeof aObject.name == "function"(または期待している型)を使用しても構いません。
  • JavaScript 配列を作るときは混乱しやすい new Array(value1, value2) よりも [value1, value2] を使用してください。[value] は常に 1 要素の配列を生成するのに対して、new Array(length) は実際には与えられた論理的な長さを持つ物理的には空の配列を生成するからです。配列用にメモリをあらかじめ割り当てることができるかは実際には保証できません。
  • JavaScript オブジェクトを作るときは { member: value, ... } を使用してください。new Object() に対して有利な点は、初期プロパティを作ることができ、さらに拡張 JavaScript 構文を使用してゲッタやセッタを定義することができることです。デフォルトのプロパティを代入する必要があるコンストラクタが定義されている場合、オブジェクトリテラルをプロトタイププロパティに代入するようにしてください。
function SupportsString(data) {
  this.data = data;
}
SupportsString.prototype = {
  toString: function toString() {
    return data;
  }
};
  • 正規表現を使用してください。だだし賢く使用してください。例えば、aString がホワイトスペースではないことをチェックするには /\S/.test(aString); を使用してください。結果の位置を知る必要がある場合でのみ aString.search を使用してください。また、マッチした部分文字列(正規表現では括弧区切り)をまとめたい場合にのみ aString.match を使用してください。マッチするかがあらかじめわかっていない場合や、文字列の決まった既知の場所から部分文字列を抜き出す場合は正規表現はあまり便利ではありません。例えば、aString.slice(-1)aString の最後の文字を返します。aString が空であれば空の文字列を返します。
  • 真偽値を truefalse と比較しないでください。例えば、if (ioService.offline) のように書いてください。混乱のおそれがある場合、オブジェクトなら null と、数値なら 0 と、文字列なら "" と比較してください。
  • どんなときでも JavaScript リファレンス を読む価値があります。例えば、あたかも配列かのように、文字列にインデックスを付けることができるということを忘れないようにするために。

XPConnect

  • オブジェクトのメソッドやプロパティを必要以上に使用しないでください。一時変数に結果を格納しておくとより高速になります。
  • 必要のないメソッドを呼び出さないでください。例えば、1 つのウィンドウに対してならば windowManager.getEnumerator(aType).hasMoreElements() ではなく windowManager.getMostRecentWindow(aType) != null が使用できます。
  • そのインタフェースのメソッドやプロパティにアクセスする必要がないときは、インタフェースにクエリをかけないでください。オブジェクトを比較するときもオブジェクトをパラメータとして渡すときもインタフェースにクエリをかける必要はありません。(C++ では両者とも必要です。)
  • 成功しそうでないときは QueryInterface を呼び出さないでください。その代わりに instanceof を使用してください。
if (target instanceof Components.interfaces.nsIRDFResource)
  return target.Value;
if (target instanceof Components.interfaces.nsIRDFLiteral)
  return target.Value;
return null;
  • QueryInterface の戻り値をテストしないでください。成功すると常にオリジナルの変数を返します。XPConnect は tearoff に関してすべて知っていますし、既知のインタフェースをすべて得るために QueryInterfaceinstanceof をしたオブジェクトを変更します。
  • オブジェクトを XPCOM のメソッドに渡す際に、渡すオブジェクトが XPCOM オブジェクトかどうか確認することはしばしば有用です。そうすることで C++ のメソッドは C++ のオブジェクトにアクセスできます。しかし、これは常に必要であるとは限りません。例えば、上で宣言したオフラインオブザーバは XPCOM オブジェクトとともに登録された JavaScript のオブジェクトです。そうすることで XPCOM からのコールバックが JavaScript のメソッドを実行できます。XPCOM メソッドにはいくつかのインタフェースを実装したオブジェクトを期待したものもあります。そのために QueryInterface メソッドを使う必要が出てきます。しかし、C++ だとヘルパクラスを必要とする弱参照については、JavaScript だと至極簡単に書けます。
var weakObserver = {
  QueryInterface: function QueryInterface(aIID) {
    if (aIID.equals(Components.interfaces.nsIObserver) ||
        aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
        aIID.equals(Components.interfaces.nsISupports))
       return this;
    throw Components.results.NS_NOINTERFACE;
  },
  observe: function observe(aSubject, aTopic, aState) {
  }
}
  • XPCOM メソッドを宣言するときは、インタフェース定義で使われているのと同じメソッドのパラメータの名前を使用するようにしてください。

DOM 要素

  • DOM 要素は単にあらかじめいくつかインタフェースが備わっている XPCOM オブジェクトです。
  • ある属性が存在しているかを確かめるのに getAttribute を呼び出さないでください。その代わりに hasAttribute を呼び出してください。
  • next/previousSibling と一緒に first/lastChild を使うのではなく、childNodes に対するループを使うようにしてください。ただし、childNodes.length > 0 ではなく hasChildNodes() を使用してください。同様に、document.getElementsByTagName(aTag).length > 0 ではなく document.getElementsByTagName(aTag).item(0) != null を使用するようにしてください。
  • tagName ではなく localName を使用するようにしてください。
  • XUL の要素にはプロパティに対応した属性がたくさんあります。こうなっているのには理由がありますので、どんどん使用してください。そのプロパティは以下のものです。
    • id
    • align
    • dir
    • flex
    • orient
    • pack
    • observes
    • contextMenu
    • tooltip
    • width
    • height
    • minWidth
    • minHeight
    • maxWidth
    • maxHeight
    • persist
    • left
    • top
    • datasources
    • ref
    • tooltipText
    • statusText
    • allowEvents
  • XUL は ordinal 属性も対応させていますが、これがない場合のデフォルトは "1" です。
  • XUL は class 属性も対応させていますが、残念ながら class は予約済みの識別子であるためにプロパティは className という名前です。(プロパティは ["class"] として実装できそうですが、見た目が悪いです。)
  • XUL は hidden および collapsed 属性もプロパティに対応させていますが、上のリストがすべて文字列のプロパティであるのに対してこれらは真偽値のプロパティであることに注意してください。
  • XBL バインディングを使うことで XUL は他の便利なプロパティやメソッドも対応させられますが、これは要素によって異なります。
  • 最高のパフォーマンスを求めるために、すべての重要な要素に id を付けてください。しかしながら、タグ名によって要素を特定することに加えて、XUL は属性によって要素を特定することもできます。使える属性は文書の要素によって異なります。
  • <del style="display: none">event.keyCode == 13 ではなく event.keyCode == KeyEvent.DOM_VK_RETURN のような DOM 定数を使用するのを忘れないでください。</del><ins style="text-decoration: none">現在、KeyEvent定数はDOM仕様から外れたため(既に他のブラウザでもサポートされていない場合が多い)、数値を用いて比較するのが妥当です。</ins>

インデントの例

妥当なインデントの例:

var result = prompt(aMessage,
                    aInitialValue,
                    aCaption);

var IOService = Components.classes["@mozilla.org/network/io-service;1"]
                          .getService(Components.interfaces.nsIIOService);

XPCOM コンポーネントの初期化の妥当なスタイル:

var IOService = Components.classes["@mozilla.org/network/io-service;1"]
                          .getService(Components.interfaces.nsIIOService);

const Ci = Components.interfaces;
const Cc = Components.classes;

var IOService = Cc["@mozilla.org/network/io-service;1"].
                getService(Ci.nsIIOService);

var IOService = Cc["@mozilla.org/network/io-service;1"].
                  getService(Ci.nsIIOService);
Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService)
                                   .logStringMessage(aMsg);

ツール

  • JST Review - C++ 用ではありますが、基本的な JS のルール(タブ、行の長さなど)にも対応しています。

参考資料

Document Tags and Contributors

Contributors to this page: Drry, namusyaka, Electrolysis
最終更新者: namusyaka,