第26章 メタコードスタイルガイド
目次
書籍を購入する
(広告です。ブロックしないでください。)

第26章 メタコードスタイルガイド

JavaScriptには多くの素晴らしいスタイルガイドがあります。そのため、さらに別のスタイルガイドを作成する必要はありません。代わりに、この章ではメタスタイルルールについて説明し、既存のスタイルガイドと確立されたベストプラクティスを調査します。また、私が好む、より議論の余地のあるプラクティスについても言及します。既存のスタイルガイドを置き換えるのではなく、補完することを目的としています。

既存のスタイルガイド

これは私が好むスタイルガイドです:

さらに、メタに関する2つのスタイルガイドがあります

一般的なヒント

このセクションでは、一般的なコード記述のヒントについて説明します。

コードは一貫性のあるものにする

一貫性のあるコードを書くための重要なルールは2つあります。最初のルールは、新しいプロジェクトを開始する場合、スタイルを考え出し、文書化し、どこでもそれを守ることです。チームが大きいほど、JSHintなどのツールを使用して、スタイルの遵守を自動的にチェックすることが重要になります。スタイルに関しては、多くの決定を下す必要があります。それらのほとんどは、一般的に合意された回答があります。その他はプロジェクトごとに定義する必要があります。例えば:

  • 空白の量 (括弧の後、ステートメントの間など)
  • インデント (例: インデントレベルごとのスペース数)
  • varステートメントをどのように、どこに書くか

2番目のルールは、既存のプロジェクトに参加する場合、そのルールを厳密に守ることです (たとえ同意しなくても)。

コードは理解しやすいものにする

デバッグはプログラムを書くことの2倍難しいことは誰もが知っています。そのため、書くときにできるだけ賢くしていると、どうやってデバッグするのでしょうか?—Brian Kernighan

ほとんどのコードでは、読むのにかかる時間は書くのにかかる時間よりもはるかに長くなります。そのため、前者をできるだけ簡単にすることが重要です。そのためのガイドラインをいくつか紹介します

短い方が常に良いとは限りません
書く量を増やすことで、実際には読むのが速くなる場合があります。2つの例を考えてみましょう。まず、馴染みのあるものは理解しやすいです。これは、馴染みのある、少し冗長な構造を使用する方が好ましい場合があることを意味します。次に、人間は文字ではなくトークンを読みます。したがって、redBalloonrdBllnよりも読みやすいです。
良いコードは教科書のようなものです

ほとんどのコードベースは、新しいアイデアや概念でいっぱいです。つまり、コードベースで作業したい場合は、それらのアイデアと概念を学ぶ必要があります。教科書とは対照的に、コードの追加の課題は、人々がそれを線形に読まないことです。彼らはどこにでも飛び込み、何が起こっているのかを大まかに理解できるはずです。コードベースの3つの部分が役立ちます

賢くしないでください。考えさせないでください。
言語の深い知識を使用して、印象的な簡潔さを実現する巧妙なコードがたくさんあります。このようなコードは通常、パズルのようなもので、理解するのが難しいです。もし人々がそのようなコードを理解できないのであれば、おそらく彼らは本当に最初にJavaScriptを学ぶべきだという意見に遭遇するでしょう。しかし、これはそういうことではありません。あなたがどれほど賢くても、他人の精神世界に入ることは常に難しいことです。そのため、単純なコードは愚かではなく、すべてを理解しやすくするために努力が注がれたコードです。「他人」には、将来の自分自身も含まれます。過去の巧妙な考えが、現在の自分には理解できないことがよくあります。
速度やコードサイズを最適化することを避けましょう

多くの賢さは、これらの最適化に向けられています。ただし、通常は必要ありません。一方では、JavaScriptエンジンはますますスマートになり、確立されたパターンに従うコードの速度を自動的に最適化します。一方、縮小ツール (第32章) は、コードを可能な限り小さくするように書き直します。どちらの場合も、ツールはあなたのために賢いため、あなたが賢くなる必要はありません。

コードのパフォーマンスを最適化する以外に選択肢がない場合があります。その場合は、必ず正しい部分を測定して最適化してください。ブラウザでは、問題は多くの場合DOMとHTMLに関連しており、言語自体には関連していません。

一般的に受け入れられているベストプラクティス

JavaScriptプログラマーの大多数は、次のベストプラクティスに同意しています。

ブレーススタイル

中括弧がコードブロックを区切る言語では、ブレーススタイルは、それらの中括弧をどこに配置するかを決定します。Cのような言語 (JavaやJavaScriptなど) では、2つのブレーススタイルが最も一般的です。Allmanスタイルと1TBSです。

Allmanスタイル

ステートメントにブロックが含まれている場合、そのブロックはステートメントの先頭とはやや独立したものと見なされます。開始中括弧は、先頭と同じインデントレベルで、独自の行にあります。例えば:

// Allman brace style
function foo(x, y, z)
{
    if (x)
    {
        a();
    }
    else
    {
        b();
        c();
    }
}

1TBS (One True Brace Style)

ここでは、ブロックはステートメントのヘッダーとより密接に関連付けられています。同じ行のヘッダーの後に開始します。制御フローステートメントの本体は、単一のステートメントしかない場合でも、常に中括弧で囲みます。例えば:

// One True Brace Style
function foo(x, y, z) {
    if (x) {
        a();
    } else {
        b();
        c();
    }
}

1TBSは、(古い) K&R (Kernighan and Ritchie) スタイルの変種です。[21] K&Rスタイルでは、関数はAllmanスタイルで記述され、単一ステートメントのthenケースなど、必要のない場合は中括弧が省略されます。

// K&R brace style
function foo(x, y, z)
{
    if (x)
        a();
    else {
        b();
        c();
    }
}

JavaScript

JavaScriptの世界でのデファクトスタンダードは1TBSです。Javaから継承されており、ほとんどのスタイルガイドで推奨されています。その理由の1つは客観的なものです。オブジェクトリテラルを返す場合は、キーワードreturnと同じ行に開始中括弧を配置する必要があります (そうでない場合、自動セミコロン挿入はreturnの後にセミコロンを挿入し、何も返されないことを意味します。落とし穴: ASIは予期せずステートメントを中断する可能性がありますを参照)。

return {
    name: 'Jane'
};

明らかに、オブジェクトリテラルはコードブロックではありませんが、両方のフォーマットが同じであれば、物事はより一貫性があり、ミスをする可能性が低くなります。

私の個人的なスタイルと好みは

  • 1TBSです (可能な限り中括弧を使用することを意味します)。
  • 例外として、ステートメントを1行で記述できる場合は中括弧を省略します。例えば

    if (x) return x;

コンストラクターよりもリテラルを優先する

リテラルによっても作成できるオブジェクトを生成するコンストラクターがいくつかあります。後者の方が通常は良い選択です。

var obj = new Object(); // no
var obj = {}; // yes

var arr = new Array(); // no
var arr = []; // yes

var regex = new RegExp('abc'); // avoid if possible
var regex = /abc/; // yes

コンストラクターArrayを使用して、指定された要素を持つ配列を作成しないでください。要素で配列を初期化する (避ける!)で、その理由を説明しています。

var arr = new Array('a', 'b', 'c'); // never ever
var arr = [ 'a', 'b', 'c' ]; // yes

賢くならないでください

このセクションでは、推奨されない賢さの例を収集しています。

条件演算子

条件演算子をネストしないでください:

// Don’t:
return x === 0 ? 'red' : x === 1 ? 'green' : 'blue';

// Better:
if (x === 0) {
    return 'red';
} else if (x === 1) {
    return 'green';
} else {
    return 'blue';
}

// Best:
switch (x) {
    case 0:
        return 'red';
    case 1:
        return 'green';
    default:
        return 'blue';
}

ifステートメントの省略

論理演算子を使用してifステートメントを省略しないでください:

foo && bar(); // no
if (foo) bar(); // yes

foo || bar(); // no
if (!foo) bar(); // yes

インクリメント演算子

可能であれば、インクリメント演算子 (++) とデクリメント演算子 (--) をステートメントとして使用します。式として使用しないでください。後者の場合、それらは値を返しますが、ニーモニックはありますが、何が起こっているのかを理解するために考える必要があります。

// Unsure: what is happening?
return ++foo;

// Easy to understand
++foo;
return foo;

undefinedの確認

if (x === void 0) x = 0; // not necessary in ES5
if (x === undefined) x = 0; // preferable

ECMAScript 5以降、2番目の確認方法の方が優れています。undefinedの変更で、その理由を説明しています。

数値を整数に変換する

return x >> 0; // no
return Math.round(x); // yes

シフト演算子を使用して、数値を整数に変換できます。ただし、通常は、Math.round()などのより明示的な代替手段を使用する方が適切です。整数への変換では、整数への変換のすべての方法の概要を説明しています。

許容される賢さ

JavaScriptでは、賢さが確立されたパターンになっている場合は、賢くすることができます。

デフォルト値

Or (||) 演算子を使用してデフォルト値を提供することは、一般的なパターンです。たとえば、パラメーターの場合:

function f(x) {
    x = x || 0;
    ...
}

詳細とその他の例については、パターン: デフォルト値の提供を参照してください。

汎用メソッド

メソッドを汎用的に使用する場合は、Object.prototype{} と省略できます。次の2つの式は同等です。

Object.prototype.hasOwnProperty.call(obj, propKey)
{}.hasOwnProperty.call(obj, propKey)

また、Array.prototype [] と省略できます。

Array.prototype.slice.call(arguments)
[].slice.call(arguments)

この件については、私は賛否両論です。これはハックです(インスタンスを介してプロトタイププロパティにアクセスしています)。しかし、コードがすっきりし、将来的にはエンジンがこのパターンを最適化すると予想しています。

ECMAScript 5: トレーリングカンマ

ECMAScript 5 では、オブジェクトリテラル内のトレーリングカンマが有効です。

var obj = {
    first: 'Jane',
    last: 'Doe', // legal: trailing comma
};

ECMAScript 5: 予約語

ECMAScript 5 では、予約語(new など)をプロパティキーとして使用することもできます。

> var obj = { new: 'abc' };
> obj.new
'abc'

議論の余地のあるルール

私が好む、もう少し議論の余地のある規約をいくつか見てみましょう。

構文

構文規則から始めます。

タイトな空白

私は比較的タイトな空白が好きです。モデルは英語の書き方に倣います。開き括弧の後と閉じ括弧の前にはスペースを入れず、カンマの後にはスペースを入れます。

var result = foo('a', 'b');
var arr = [ 1, 2, 3 ];
if (flag) {
    ...
}

無名関数の場合、Douglas Crockford のルールに従って、キーワード function の後にスペースを入れます。その理由は、名前付き関数式から名前を削除した場合と同じ見た目になるためです。

function foo(arg) { ... }  // named function expression
function (arg) { ... }     // anonymous function expression
インデントレベルごとに4つのスペース
私が見ているほとんどのコードは、インデントにスペースを使用しています。これは、タブがアプリケーションやオペレーティングシステムによって表示方法が大きく異なるためです。インデントをより見やすくするために、インデントレベルごとに4つのスペースを推奨します。
条件演算子を括弧で囲む

演算子のスコープがわかりやすくなるため、読みやすくなります。

return result ? result : theDefault;  // no
return (result ? result : theDefault);  // yes

変数

次に、変数の規則について説明します。

1行につき1つの変数宣言

私は、1つの宣言で複数の変数を宣言しません。

// no
var foo = 3,
    bar = 2,
    baz;

// yes
var foo = 3;
var bar = 2;
var baz;

このアプローチの利点は、行の削除、挿入、並べ替えが簡単になり、行が自動的に正しくインデントされることです。

変数宣言をローカルに保つ
関数が長すぎない場合(そうでないはずです)、巻き上げに関してそれほど注意を払わず、var 宣言がブロックスコープであるかのように扱うことができます。つまり、変数を使用するコンテキスト(ループ内、then ブロックまたは else ブロック内など)で変数を宣言できます。この種のローカルカプセル化により、コードフラグメントを個別に理解しやすくなります。また、コードフラグメントを削除したり、他の場所に移動したりすることも容易になります。
ブロック内にある場合は、そのブロック内に留まる

前のルールの補足として、同じ変数を2つの異なるブロックで2回宣言しないでください。例えば

// Don’t do this
if (v) {
    var x = v;
} else {
    var x = 10;
}
doSomethingWith(x);

上記のコードは、次のコードと同じ効果と意図を持っているため、そのように記述する必要があります。

var x;
if (v) {
    x = v;
} else {
    x = 10;
}
doSomethingWith(x);

オブジェクト指向

次に、オブジェクト指向に関する規則について説明します。

他のインスタンス作成パターンよりもコンストラクターを優先する

以下のことをお勧めします。

  • 常にコンストラクターを使用する。
  • インスタンスを作成するときは常に new を使用する。

そうする主な利点は次のとおりです。

  • コードが JavaScript の主流により適合し、フレームワーク間での移植性が高くなります。
  • 最新のエンジンでは、コンストラクターのインスタンスの使用は非常に高速です(たとえば、隠しクラスを介して)。
  • 今後の ECMAScript 6 のデフォルトの継承構造であるクラスは、コンストラクターに基づいています。

コンストラクターの場合、厳格モードを使用することが重要です。これは、インスタンス化のために new 演算子を忘れることから保護するためです。また、コンストラクターで任意のオブジェクトを返すことができることに注意してください。コンストラクターの使用に関するその他のヒントは、コンストラクター実装のヒントに記載されています。

プライベートデータにクロージャを使用しない
オブジェクトのプライベートデータを完全に安全にするには、クロージャを使用する必要があります。そうでない場合は、通常のプロパティを使用できます。一般的な方法の1つは、プライベートプロパティの名前にアンダースコアを付けることです。クロージャの問題点は、コードがより複雑になり(すべてのメソッドをインスタンスに配置しない限り、これは慣用的ではなく、低速です)、速度が低下することです(クロージャ内のデータへのアクセスは、現在、プロパティへのアクセスよりも低速です)。データを非公開にするでは、このトピックについて詳しく説明しています。
コンストラクターに引数がない場合は括弧を付ける

括弧を付けた方が、このようなコンストラクターの呼び出しが見やすくなると感じています。

var foo = new Foo;  // no
var foo = new Foo();  // yes
演算子の優先順位に注意する

2つの演算子が互いに競合しないように括弧を使用してください。結果は必ずしも期待どおりにならない場合があります。

> false && true || true
true
> false && (true || true)
false
> (false && true) || true
true

instanceof は特に注意が必要です。

> ! {} instanceof Array
false
> (!{}) instanceof Array
false
> !({} instanceof Array)
true

ただし、コンストラクターの後のメソッド呼び出しには問題がないと考えています。

new Foo().bar().baz();  // ok
(new Foo()).bar().baz();  // not necessary

その他

このセクションでは、さまざまなヒントを紹介します。

型変換

BooleanNumberString()Object()(関数として使用—これらの関数をコンストラクターとして使用しないでください)を介して値を型に変換します。その理由は、この規則の方が説明的であるためです。

> +'123'  // no
123
> Number('123')  // yes
123

> ''+true  // no
'true'
> String(true)  // yes
'true'
暗黙的なパラメーターとして this を使用しない

this は、現在のメソッド呼び出しのレシーバーのみを参照する必要があります。暗黙的なパラメーターとして乱用しないでください。その理由は、このような関数は呼び出しと理解が容易になるためです。また、オブジェクト指向メカニズムと関数型メカニズムを分けておくことも好きです。

// Avoid:
function handler() {
    this.logError(...);
}

// Prefer:
function handler(context) {
    context.logError(...);
}
in および hasOwnProperty を使用してプロパティの存在を確認します( プロパティの反復と検出を参照)。

これは、undefined と比較したり、truthiness を確認したりするよりも、わかりやすく安全です。

// All properties:
if (obj.foo)  // no
if (obj.foo !== undefined)  // no
if ('foo' in obj) ... // yes

// Own properties:
if (obj.hasOwnProperty('foo')) ... // risky for arbitrary objects
if (Object.prototype.hasOwnProperty.call(obj, 'foo')) ... // safe
フェイルファースト
可能であれば、フェイルファーストを行い、サイレントに失敗しないことが最善です。JavaScript はそれほど寛容ではありません(たとえば、ゼロ除算)。これは、ECMAScript の最初のバージョンに例外がなかったためです。たとえば、値を強制変換しないでください。例外をスローします。ただし、コードが本番環境にある場合は、エラーから正常に回復する方法を見つける必要があります。

結論

スタイルの問題を検討する場合は、常に次の質問をしてください。コードを理解しやすくするにはどうすればよいですか?賢くあろうとする誘惑に抵抗し、機械的な賢さのほとんどを JavaScript エンジンとミニファイアーに任せましょう(第32章を参照)。



[21] 1TBS と K&R は同義語であり、1TBS は K&R を冗談めかして呼ぶ方法であると言う人もいます。

次: 27. デバッグのための言語メカニズム