#慣用的なJavaScript, 矛盾しないコードの書き方
これは現在使っているドキュメントです。私達が使っているコードを向上させるためのアイディアはいつでも歓迎です。意見を述べるには、fork, clone, branch, commit, push, pull requestなどをしてください。
- Rick Waldron @rwaldron, github
- Mathias Bynens @mathias, github
- Schalk Neethling @ossreleasefeed, github
- Kit Cambridge @kitcambridge, github
- Raynos github
- Matias Arriola @MatiasArriola, github
- Idan Gazit @idangazit, github
- Leo Balter @leobalter, github
- Breno Oliveira @garu_rj, github
- Leo Beto Souza @leobetosouza, github
- EngForDev engfordev - Hwan Min Hong / MinTaek Kwon @leoinsight / Tw Shim @marocchino, github / Nassol Kim @nassol99, github / Juntai Park @rkJun, github / Minkyu Shim / Gangmin Won / Justin Yoo @justinchronicle / Daeyup Lee
“成功するプロジェクトのために良いスチュワードの一部であることは、あなた自身のためのライティングコードは悪いアイディアであることを認識すること?もし大勢の人があなたのコードを使うなら、あなたのコードは最大限にわかりやすく、そして仕様を巧く潜り抜けるためのあなたの個人的な好みは使用するべきではないです。” - Idan Gazit.
レバレッジ・コード・クオリティー・ツール by Anton Kovalyov
以下はの項目は考慮すべきです。 (1) 不完全で (2) 必読 私は常に原作者の書き方のスタイルに従う必要はないと思いますが、一つだけ確かなのは、彼らは必ず一貫しています。さらにはそれらは言語上の通例です。
- Eloquent JavaScript
- JavaScript, JavaScript
- Rebecca Murphey or Adventures in JavaScript Development
- Perfection Kills
- Douglas Crockford's Wrrrld Wide Web
プロジェクトは製品で使用する準備のためのLintやテスト、圧縮などのいくつかの典型的な方法を含むようにするべきです。この作業においてBen Alman の gruntは、他に比べようのないくらい良いとされ、このレポジトリの”kits/”ディレクトリに置き換えられています。
プロジェクトは _必ず_何らかの形でユニットや参照、実装、または機能テストを含まなければならないです。デモのケースを使用して”テスト”とは見做されません。以下はテストのフレームワークの一覧ですが、どれが特に支持されているとは限りません。
- 空白について
- きれいな構文
- 型チェック (Courtesy jQuery Core Style Guidelines)
- 条件付きの評価
- 実用的なスタイル
- ネーミング
- その他の情報
- ネイティブとホストオブジェクト
- コメント
- 一つの言語コード
-
空白について * スペースとタブは絶対に混ぜない
* プロジェクトを始めるときコードを書き始める前に、空白かタブのどちらかを選択しなさい — これはルールです。 * 可読性のために、私はエディタのインデントサイズを2文字に設定することを薦めています。 — これは1つはタブ文字を2つで示すか、空白2文字です。 * もしあなたのエディタがサポートするなら、”空白文字を表示”の設定をOnにして作業してください。このやり方の良いところは、 * 一貫性の強化 * 行末の空白文字の除去 * 空行の空白文字の除去 * CommitやDiffのときに読みやすくなる
-
A. 丸カッコ( ), 中カッコ{ }, 改行 ```javascript
// if/else/for/while/try は常に空白文字や改行、中カッコで区切られた複数行にすることで読みやすくなります。
// 2.A.1.1
// 非常に詰め込んだ構文の例
if(condition) doSomething();
while(condition) iterating++;
for(var i=0;i<100;i++) someIterativeFn();
// 2.A.1.1
// 可読性のために空白文字や改行を使用しましょう
if ( condition ) {
// ステートメント
}
while ( condition ) {
// ステートメント
}
for ( var i = 0; i < 100; i++ ) {
// ステートメント
}
// さらに良い方法:
var i,
length = 100;
for ( i = 0; i < length; i++ ) {
// ステートメント
}
// または...
var i = 0,
length = 100;
for ( ; i < length; i++ ) {
// ステートメント
}
var prop;
for ( prop in object ) {
// ステートメント
}
if ( true ) {
// ステートメント
} else {
// ステートメント
}
```
B. 値の割り当て, 宣言, 関数の名付け方, 関数の表し方, 関数のコンストラクタ
```javascript
// 2.B.1.1
// 変数
var foo = "bar",
num = 1,
undef;
// 記号表記
var array = [],
object = {};
// 2.B.1.2
// 可読性を向上させるために、スコープ(関数)に対して1つだけの`var`を使うことで、
// たくさんの宣言文を使うことを避ける事ができます。(varを入力するためにキーを押す回数が節約できます)
// 悪い例
var foo = "";
var bar = "";
var qux;
// 良い例
var foo = "",
bar = "",
quux;
// または..
var // これらのコメント
foo = "",
bar = "",
quux;
// 2.B.1.3
// varステートメントは常に各スコープ(関数)の冒頭にあるようにすべきです。
// constもECMAScript6から冒頭に定義するようになってます。
// 悪い例
function foo() {
// いくつかのステートメントをここに記述
var bar = "",
qux;
}
// 良い例
function foo() {
var bar = "",
qux;
// 全てのステートメントは変数の定義の後にするべき
}
```
```javascript
// 2.B.2.1
// 名前付き関数の定義
function foo( arg1, argN ) {
}
// 使い方
foo( arg1, argN );
// 2.B.2.2
// 名前付き関数の定義
function square( number ) {
return number * number;
}
// 使い方
square( 10 );
// 継続したいい流れのスタイル function square( number, callback ) { callback( number * number ); }
square( 10, function( square ) {
// コールバック・ステートメント
});
// 2.B.2.3
// 関数式
var square = function( number ) {
// 重要な関連する何かを返す
return number * number;
};
// 識別子を使った関数式
// この好ましい形は関数の中でその関数を呼び出すこととスタックトレースのなかで
// 関数を区別することができる識別子(アイデンティティ)を持つことができるという付加価値があります。 var factorial = function( number ) { if ( number < 2 ) { return 1; }
return number * factorial( number-1 );
};
// 2.B.2.4
// コンストラクタを使った定義
function FooBar( options ) {
this.options = options;
}
// 使い方
var fooBar = new FooBar({ a: "alpha" });
fooBar.options;
// { a: "alpha" }
```
C. 例外, 多少の逸脱
```javascript
// 2.C.1.1
// コールバック付き関数
foo(function() {
// 注: 実行する関数の呼び出しの最初の円カッコと
// ”function”の間には余分な空白文字を入れないこと });
// 配列を関数で受け取るときに空白文字は入れない
foo([ "alpha", "beta" ]);
// 2.C.1.2
// オブジェクトを関数で受け取るときに空白文字は入れない
foo({
a: "alpha",
b: "beta"
});
// 内カッコによるグループ化のときに空白文字は入れない
if ( !("foo" in obj) ) {
}
```
D. 一貫性は常に勝つ
セクション 2.A〜2.Cでは、空白文字のルールは、シンプルな一貫性を高めるものとして使用するように明記されています。
気を付けなければいけないのは、書式設定 (例えば”内側の空白文字”など) はオプション的に考慮すべきことであり、それだけでなく一つのスタイルはプロジェクトの全てのソースにおいて使用されるべきです。
```javascript
// 2.D.1.1
if (condition) {
// ステートメント
}
while (condition) {
// ステートメント
}
for (var i = 0; i < 100; i++) {
// ステートメント
}
if (true) {
// ステートメント
} else {
// ステートメント
}
```
E. 行末と空白行
空白はdiffを台無しにして読むことができないようにしてしまうことができます。行末の空白文字と空行の空白文字を自動的に削除するプリコミットメントを検討しましょう。
-
型チェック (Courtesy jQuery Core Style Guidelines)
3.A 実際の変数型 * String: `typeof variable === "string"` * Number: `typeof variable === "number"` * Boolean: `typeof variable === "boolean"` * Object: `typeof variable === "object"` * Array: `Array.isArray(arrayObject)` (wherever possible) * null: `variable === null` * null or undefined: `variable == null` * undefined: * Global Variables: * `typeof variable === "undefined"` * Local Variables: * `variable === undefined` * Properties: * `object.prop === undefined` * `object.hasOwnProperty( prop )` * `"prop" in object` JavaScriptは動的型言語なので、使いやすい言語にも使いにくい言語にもなりえるので、常に「変数の型」を注意しましょう。 3.B Coerced Types 3.B 変数型の強要 以下のようなことに気をつけて下さい このHTMLを与えられたとして: ```html <input type="text" id="foo-input" value="1"> ``` ```js // 3.B.1.1 // `foo`は `0` という値で定義されていて型は`number`とします。 var foo = 0; // typeof foo; // "number" ... // あなたのコード内のどこか後の方で、`foo`をInput要素からの
// 新しい値で更新する必要があるとします。
foo = document.getElementById("foo-input").value;
// もしあなたが`typeof foo`をここでテストしたら、結果は `string`となるでしょう。
// これはあなたが`foo`をこんな風にテストするロジックだとしたら、
if ( foo === 1 ) {
importantTask();
}
// foo
が"1"を持っていたとしても、importantTask()
は決して実行されないでしょう。
// 3.B.1.2
// smart coercionを使って、単項と + または - 演算子の問題を避けることができます。
foo = +document.getElementById("foo-input").value;
^ 単項(foo) + operator は、その右サイドのオペランドを数値に変換します。
// typeof foo;
// "number"
if ( foo === 1 ) {
importantTask();
}
// `importantTask()` は呼び出されます。
```
以下は型変換に伴う一般的なケースです。
```javascript
// 3.B.2.1
var number = 1,
string = "1",
bool = false;
number;
// 1
number + "";
// "1"
string;
// "1"
+string;
// 1
+string++;
// 1
string;
// 2
bool;
// false
+bool;
// 0
bool + "";
// "false"
```
```javascript
// 3.B.2.2
var number = 1,
string = "1",
bool = true;
string === number;
// false
string === number + "";
// true
+string === number;
// true
bool === number;
// false
+bool === number;
// true
bool === string;
// false
bool === !!string;
// true
```
```javascript
// 3.B.2.3
var array = [ "a", "b", "c" ];
!!~array.indexOf( "a" );
// true
!!~array.indexOf( "b" );
// true
!!~array.indexOf( "c" );
// true
!!~array.indexOf( "d" );
// false
var num = 2.5;
parseInt( num, 10 );
// は
~~num;
// と同じです。
```
-
```javascript // 4.1.1 // 配列が長さを持つ条件式の評価の時だけ // 以下の代わりに、 if ( array.length > 0 ) ... // 次のように真偽値による評価にすることができます。 if ( array.length ) ... // 4.1.2 // instead of this: // 配列が空の条件式を評価するときだけ、 // 以下の代わりに if ( array.length === 0 ) ... // 次のように真偽値による評価にすることができます。 if ( !array.length ) ... // 4.1.3 // 文字列が空でない条件式を評価するときだけ、 // 以下の代わりに if ( string !== "" ) ... // 次のようにtrueによる評価にすることができます。 if ( string ) ... // 4.1.4 // 配列が空の条件式を評価するときだけ、 // 以下の代わりに if ( string === "" ) ... // 次のようにfalseによる評価にすることができます。 if ( !string ) ... // 4.1.5 // 参照がtrueかどうかの条件式を評価するときだけ、 // 以下の代わりに if ( foo === true ) ... // もともとの機能をうまく利用して評価をするという手もあります。 if ( foo ) ... // 4.1.6 // 参照falseかどうかの条件式を評価するときだけ、 // 以下の代わりに if ( foo === false ) ... // trueの評価に否定を使うことができます。 if ( !foo ) ... // ですが、この方法は 0, “”, null, undefined, NaNにもマッチします。 // もし_絶対に_boolean型でfalseかどうかテストしたいなら、以下を使ってください。 if ( foo === false ) ... // 4.1.7 // 参照がたぶんnullかundefinedだけど false, "" や 0でない評価をしたいとき // 次の代わりに if ( foo === null || foo === undefined ) ... // == を使った型強制の利点を活用できます。 if ( foo == null ) ... // == を使った`null`とのマッチは、`null` と `undefined`にも一致しますが // `false`, "" or 0 には一致しません。 null == undefined ``` 「常に」ベストで最も正確な評価を心がけてください - 今までカバーしてきたことはガイドラインであり、定則ではありません。 ```javascript // 4.2.1 // 型強制と 評価記号 `===` は `==` よりも好ましいです(緩い型評価が必要な場合は除く) === は型強制を行いません。つまり次のようになります。 "1" === 1; // false == は型強制を行うので、以下のようになります。 "1" == 1; // true // 4.2.2 // 真偽値(Booleans), Truthies & Falsies Booleans: true, false Truthy are: "foo", 1 Falsy are: "", 0, null, undefined, NaN, void 0 ```
-
```javascript // 5.1.1 // 実用的なモジュール (function( global ) { var Module = (function() { var data = "secret"; return { // これは何かのbooleanプロパティ bool: true, // 何かのstring値 string: "a string", // 配列プロパティ array: [ 1, 2, 3, 4 ], // オブジェクトプロパティ object: { lang: "en-Us" }, getData: function() { // `data`の現在値を取得 return data; }, setData: function( value ) { // `data`の値を設定して、それを返す return ( data = value ); } }; })(); // 他のことも起こるかもしれません // このモジュールをグローバルオブジェクトに見えるようにする global.Module = Module; })( this ); ``` ```javascript // 5.2.1 // 実用的なコンストラクタ (function( global ) { function Ctor( foo ) { this.foo = foo; return this; } Ctor.prototype.getFoo = function() { return this.foo; }; Ctor.prototype.setFoo = function( val ) { return ( this.foo = val ); }; // `new`を使わないでコンストラクタを呼ぶために、このようにすることができます。 var ctor = function( foo ) { return new Ctor( foo ); }; // このモジュールをグローバルオブジェクトに見えるようにします global.ctor = ctor; })( this ); ```
-
あなたは人間コードコンパイラ/コンプレッサではないので、それになろうとはしないでください。 以下のコードは実にひどいネーミングです。 ```javascript // 6.1.1 // 下手な名前のコードの例 function q(s) { return document.querySelectorAll(s); } var i,a=[],els=q("#foo"); for(i=0;i<els.length;i++){a.push(els[i]);} ``` 疑いなくこのようなコードを書いたことがあるはずです。それも今日で終わりにしましょう。 同じロジックを使っても、より考えられたネーミングであれば、より読みやすい構造になります。 ```javascript // 6.2.1 // 名前の向上したコードの例 function query( selector ) { return document.querySelectorAll( selector ); } var idx = 0, elements = [], matches = query("#foo"), length = matches.length; for( ; idx < length; idx++ ){ elements.push( matches[ idx ] ); } ``` 追加のネーミングのポイントは: ```javascript // 6.3.1 // 文字列のネーミング `dog` は文字列 // 6.3.2 // 配列のネーミング `dogs` は文字列`dog`の配列 // 6.3.3 // 関数、オブジェクト、インスタンスなどのネーミング キャメルケースを使って関数と変数の宣言をする(例:camelCase) // 6.3.4 // コンストラクタ、プロトタイプなどのネーミング パスカルケースを使ってコンストラクタ関数の宣言をする(例:PascalCase) // 6.3.5 // 正規表現のネーミング rDesc = //; // 6.3.6 // Google Clouser Libraryスタイルのガイド functionNamesLikeThis; // 関数はこのように書いてください variableNamesLikeThis; // 変数はこのように書いてください ConstructorNamesLikeThis; // コンストラクタはこのように書いてください EnumNamesLikeThis; // 列挙関数はこのように書いてください methodNamesLikeThis; // メソッド名はこのように書いてください SYMBOLIC_CONSTANTS_LIKE_THIS; // シンボル定数はこのように書いてください ```
-
このセクションは定説と考えるべきでないアイディアとコンセプトを説明するのが目的です。一般的なJavaScriptのプログラミングをするためのより良い方法を見つける試行錯誤を奨励するために存在します。 A. `switch` の使用は避けるべきで、モダンなトレースはスイッチステートメントを関数を隠してしまいます。 これは最新のFirefox と Chromeでは`switch` ステートメントの実行が極端に改善しているようです。 http://jsperf.com/switch-vs-object-literal-vs-module 注目すべき改善は、ここにも同様に見ることができます。 https://github.com/rwldrn/idiomatic.js/issues/13 ```javascript // 7.A.1.1 // スイッチステートメントの例 switch( foo ) { case "alpha": alpha(); break; case "beta": beta(); break; default: // デフォルト処理はここ。 break; } // 7.A.1.2 // 良いアプローチは、オブジェクト定数、もしくはモジュールを使うことです。 var switchObj = { alpha: function() { // statements // a return }, beta: function() { // statements // a return }, _default: function() { // statements // a return } }; var switchModule = (function () { return { alpha: function() { // statements // a return }, beta: function() { // statements // a return }, _default: function() { // statements // a return } }; })(); // 7.A.1.3 // もし`foo`が `switchObj` または `switchModule`のプロパティなら、メソッドのように実行できます。 ( Object.hasOwnProperty.call( switchObj, foo ) && switchObj[ foo ] || switchObj._default )( args ); ( Object.hasOwnProperty.call( switchObj, foo ) && switchModule[ foo ] || switchModule._default )( args ); // もしあなたが(コーディングするときに)`foo`の値がそうなるはずだと信じるなら、ORチェックすら省略して // 実行だけ残すことができます。 switchObj[ foo ]( args ); switchModule[ foo ]( args ); // このパターンはコードの再利用性も促進します。 ``` B. コードの前半で早めにreturnすることで、コードを読みやすくして、僅かですがパフォーマンスを促進します。 ```javascript // 7.B.1.1 // 悪い例: function returnLate( foo ) { var ret; if ( foo ) { ret = "foo"; } else { ret = "quux"; } return ret; } // 良い例: function returnEarly( foo ) { if ( foo ) { return "foo"; } return "quux"; } ```
-
基本中の基本はこれです: ### つまらない無意味なことはするな、そうすれば全てが上手くいく このコンセプトを補強するために、次のプレゼンテーションを観てください。 #### “Everything is Permitted: Extending Built-ins(全てのことは認められている: 組み込み機能の拡張)” by Andrew Dupont (JSConf2011, Portland, Oregon) <iframe src="http://blip.tv/play/g_Mngr6LegI.html" width="480" height="346" frameborder="0" allowfullscreen></iframe><embed type="application/x-shockwave-flash" src="http://a.blip.tv/api.swf#g_Mngr6LegI" style="display:none"></embed> http://blip.tv/jsconf/jsconf2011-andrew-dupont-everything-is-permitted-extending-built-ins-5211542
-
* JSDoc スタイルは良いです (Closure Compilerの型の方法は hints++) * コードの上の一行で書くコメントは題名です * 複数ラインはGoodultiline is go * 行末のコメントは禁止!
-
プログラムはどんな言語であれ、メンテナンスをする人(達)によって書き取らせるように、一つの言語で書かれるべきです。
このドキュメントを基本ガイドとして引用するどんなプロジェクトは、その他で明確に指定された場合を除き、コンマ・ファースト形式(配列などでコンマが先に来る形式)は受け入れられません。
See: https://mail.mozilla.org/pipermail/es-discuss/2011-September/016805.html Notable: "That is horrible, and a reason to reject comma first.", "comma-first still is to be avoided"
参照: https://mail.mozilla.org/pipermail/es-discuss/2011-September/016805.html 注目点: "That is horrible, and a reason to reject comma first.(これはひどい、だからコンマ・ファーストは拒否する)", "comma-first still is to be avoided(コンマ・ファーストはまだ避けるべきだ)"