Featured image of post Airbnb JavaScript スタイルガイド

Airbnb JavaScript スタイルガイド

Airbnb JavaScript スタイルガイド

Photo by Pankaj Patel on Unsplash

https://github.com/jigsawye/javascript

Airbnb JavaScript スタイルガイド()

JavaScript に対する、ほぼ妥当なアプローチ

Downloads Gitter

その他のスタイルガイド

Airbnb JavaScript Style Guide から翻訳されました。

  • 1.1 プリミティブ: プリミティブには直接アクセスします。
  • string
  • number
  • boolean
  • null
  • undefined
const foo = 1;
let bar = foo;

bar = 9;

console.log(foo, bar); // => 1, 9
  • 1.2 複合: 複合型には参照でアクセスします。
  • object
  • array
  • function
const foo = [1, 2];
const bar = foo;

bar[0] = 9;

console.log(foo[0], bar[0]); // => 9, 9

参照

なぜなら、参照を再割り当てできないようにすることで、バグを減らし、コードを理解しやすくするためです。

// bad
var a = 1;
var b = 2;

// good
const a = 1;
const b = 2;
  • 2.2 もし参照を再割り当てする必要がある場合は、var の代わりに let を使用してください。eslint: no-var jscs: disallowVar

なぜなら、letvar のような関数スコープではなく、ブロックスコープだからです。

// bad
var count = 1;
if (true) {
    count += 1;
}

// good, use the let.
let count = 1;
if (true) {
    count += 1;
}
  • 2.3 letconst はどちらもブロックスコープであることに注意してください。
// const と let は、それらが定義されたブロック内でのみ存在します。
{
    let a = 1;
    const b = 1;
}
console.log(a); // ReferenceError
console.log(b); // ReferenceError

オブジェクト

  • 3.1 オブジェクトを作成するときはリテラル構文を使用してください。eslint rules: no-new-object.
// bad
const item = new Object();

// good
const item = {};
// bad
const superman = {
    default: { clark: 'kent' },
    private: true,
};

// good
const superman = {
    defaults: { clark: 'kent' },
    hidden: true,
};
// bad
const superman = {
    class: 'alien',
};

// bad
const superman = {
    klass: 'alien',
};

// good
const superman = {
    type: 'alien',
};

  • 3.4 動的なプロパティ名でオブジェクトを作成する場合は、計算されたプロパティ名(computed property names)を使用してください。

なぜなら、オブジェクトのすべてのプロパティを1か所で定義できるからです。


function getKey(k) {
    return `a key named ${k}`;
}

// bad
const obj = {
    id: 5,
    name: 'San Francisco',
};
obj[getKey('enabled')] = true;

// good
const obj = {
    id: 5,
    name: 'San Francisco',
    [getKey('enabled')]: true,
};

// bad
const atom = {
    value: 1,

    addValue: function (value) {
    return atom.value + value;
    },
};

// good
const atom = {
    value: 1,

    addValue(value) {
    return atom.value + value;
    },
};

なぜなら、より短く記述でき、明確だからです。

const lukeSkywalker = 'Luke Skywalker';

// bad
const obj = {
    lukeSkywalker: lukeSkywalker,
};

// good
const obj = {
    lukeSkywalker,
};
  • 3.7 短縮表記のプロパティは、オブジェクト宣言の先頭にグループ化してください。

なぜなら、どのプロパティが短縮表記を使用しているかが分かりやすくなるからです。

const anakinSkywalker = 'Anakin Skywalker';
const lukeSkywalker = 'Luke Skywalker';

// bad
const obj = {
    episodeOne: 1,
    twoJediWalkIntoACantina: 2,
    lukeSkywalker,
    episodeThree: 3,
    mayTheFourth: 4,
    anakinSkywalker,
};

// good
const obj = {
    lukeSkywalker,
    anakinSkywalker,
    episodeOne: 1,
    twoJediWalkIntoACantina: 2,
    episodeThree: 3,
    mayTheFourth: 4,
};

なぜなら、一般的に主観的に読みやすいと考えられているからです。また、シンタックスハイライトが向上し、多くの JS エンジンで最適化されやすくなります。

// bad
const bad = {
    'foo': 3,
    'bar': 4,
    'data-blah': 5,
};

// good
const good = {
    foo: 3,
    bar: 4,
    'data-blah': 5,
};

配列

// bad
const items = new Array();

// good
const items = [];
  • 4.2 配列にアイテムを追加するときは、直接代入するのではなく Array#push を使用してください。
const someStack = [];

// bad
someStack[someStack.length] = 'abracadabra';

// good
someStack.push('abracadabra');

  • 4.3 配列をコピーするには、スプレッド演算子 ... を使用してください。
// bad
const len = items.length;
const itemsCopy = [];
let i;

for (i = 0; i < len; i++) {
    itemsCopy[i] = items[i];
}

// good
const itemsCopy = [...items];
  • 4.4 配列のようなオブジェクトを配列に変換するには、Array#from を使用してください。
const foo = document.querySelectorAll('.foo');
const nodes = Array.from(foo);
  • 4.5 配列メソッドのコールバックでは return 文を使用してください。ただし、関数本体が単一の文で構成されている場合(8.2 を参照)は、return を省略しても構いません。eslint: array-callback-return
// good
[1, 2, 3].map((x) => {
    const y = x + 1;
    return x * y;
});

// good
[1, 2, 3].map(x => x + 1);

// bad
const flat = {};
[[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {
    const flatten = memo.concat(item);
    flat[index] = memo.concat(item);
});

// good
const flat = {};
[[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => {
    const flatten = memo.concat(item);
    flat[index] = flatten;
    return flatten;
});

// bad
inbox.filter((msg) => {
    const { subject, author } = msg;
    if (subject === 'Mockingbird') {
    return author === 'Harper Lee';
    } else {
    return false;
    }
});

// good
inbox.filter((msg) => {
    const { subject, author } = msg;
    if (subject === 'Mockingbird') {
    return author === 'Harper Lee';
    }

    return false;
});

分割代入

  • 5.1 オブジェクトの複数のプロパティにアクセスして使用する場合は、オブジェクトの分割代入を使用してください。jscs: requireObjectDestructuring

なぜなら、プロパティのための一時的な参照を作成せずに済むからです。

// bad
function getFullName(user) {
    const firstName = user.firstName;
    const lastName = user.lastName;

    return `${firstName} ${lastName}`;
}

// good
function getFullName(user) {
    const { firstName, lastName } = user;
    return `${firstName} ${lastName}`;
}

// best
function getFullName({ firstName, lastName }) {
    return `${firstName} ${lastName}`;
}
const arr = [1, 2, 3, 4];

// bad
const first = arr[0];
const second = arr[1];

// good
const [first, second] = arr;
  • 5.3 複数の値を返す場合は、配列の分割代入ではなく、オブジェクトの分割代入を使用してください。

なぜなら、呼び出し元を壊すことなく、新しいプロパティを追加したり、順序を変更したりできるからです。

// bad
function processInput(input) {
    // then a miracle occurs
    return [left, right, top, bottom];
}

// the caller needs to think about the order of return data
const [left, __, top] = processInput(input);

// good
function processInput(input) {
    // then a miracle occurs
    return { left, right, top, bottom };
}

// the caller selects only the data they need
const { left, right } = processInput(input);

文字列

// bad
const name = "Capt. Janeway";

// good
const name = 'Capt. Janeway';
  • 6.2 1行が100文字を超えるような文字列は、文字列連結を使って複数行に分けて書くべきではありません。
  • 6.3 注意: 長い文字列の連結を過度に使用すると、パフォーマンスに影響を与える可能性があります。jsPerf & Discussion.
// bad
const errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.';

// bad
const errorMessage = 'This is a super long error that was thrown because \
of Batman. When you stop to think about how Batman had anything to do \
with this, you would get nowhere \
fast.';

// good
const errorMessage = 'This is a super long error that was thrown because ' +
    'of Batman. When you stop to think about how Batman had anything to do ' +
    'with this, you would get nowhere fast.';

なぜなら、テンプレート文字列は、適切な改行や文字列補間機能を備えた、読みやすく簡潔な構文を提供するからです。

// bad
function sayHi(name) {
    return 'How are you, ' + name + '?';
}

// bad
function sayHi(name) {
    return ['How are you, ', name, '?'].join();
}

// bad
function sayHi(name) {
    return `How are you, ${ name }?`;
}

// good
function sayHi(name) {
    return `How are you, ${name}?`;
}
  • 6.5 文字列に対して eval() を決して使用しないでください。脆弱性が多すぎます。

関数

なぜなら、関数宣言には名前が付いているため、スタックトレースで識別しやすくなるからです。また、関数宣言はホイスティング(巻き上げ)されますが、関数式はされません。このルールにより、アロー関数 が関数式を完全に置き換えることができます。

// bad
const foo = function () {
};

// good
function foo() {
}

なぜなら、IIFE は単一のユニットだからです。関数とその呼び出しの両方を括弧で囲むことで、これが明確になります。モジュールの世界では、通常 IIFE はもう必要ないことに注意してください。

// immediately-invoked function expression (IIFE)
(function () {
    console.log('Welcome to the Internet. Please follow me.');
}());
  • 7.3 関数以外のブロック(if、while など)の中で関数を宣言しないでください。代わりに関数を変数に割り当ててください。ブラウザでは許可されていますが、それぞれの解釈が異なります。eslint: no-loop-func

  • 7.4 注意: ECMA-262 では block を文のリストとして定義しています。関数宣言は文ではありません。この問題に関する ECMA-262 の注記を読んでください

// bad
if (currentUser) {
    function test() {
    console.log('Nope.');
    }
}

// good
let test;
if (currentUser) {
    test = () => {
    console.log('Yup.');
    };
}
  • 7.5 パラメータに arguments という名前を付けないでください。これは、すべての関数スコープに与えられる arguments オブジェクトよりも優先されてしまいます。
// bad
function nope(name, options, arguments) {
    // ...stuff...
}

// good
function yup(name, options, args) {
    // ...stuff...
}

  • 7.6 arguments を使用せず、代わりに rest 構文 ... を使用してください。prefer-rest-params

なぜなら、... はどの引数を取り出すかを明示できるからです。さらに、rest 引数は arguments のような「配列のようなオブジェクト」ではなく、本物の配列です。

// bad
function concatenateAll() {
    const args = Array.prototype.slice.call(arguments);
    return args.join('');
}

// good
function concatenateAll(...args) {
    return args.join('');
}

  • 7.7 関数の引数を変更するのではなく、デフォルトパラメータ構文を使用してください。
// really bad
function handleThings(opts) {
    // No! We shouldn't mutate function arguments.
    // Double bad: if opts is false it will be set to an object which may
    // be what you want but it can cause subtle bugs.
    opts = opts || {};
    // ...
}

// still bad
function handleThings(opts) {
    if (opts === void 0) {
    opts = {};
    }
    // ...
}

// good
function handleThings(opts = {}) {
    // ...
}
  • 7.8 デフォルトパラメータでの副作用は避けてください。

なぜなら、動作を理解するのが混乱するからです。

var b = 1;
// bad
function count(a = b++) {
    console.log(a);
}
count();  // 1
count();  // 2
count(3); // 3
count();  // 3
  • 7.9 デフォルトパラメータは常に最後に置いてください。
// bad
function handleThings(opts = {}, name) {
    // ...
}

// good
function handleThings(name, opts = {}) {
    // ...
}
  • 7.10 新しい関数を作成するために Function コンストラクタを使用しないでください。

なぜなら、この方法で関数を作成すると、eval() と同様に文字列が評価され、脆弱性を引き起こす可能性があるからです。

// bad
var add = new Function('a', 'b', 'return a + b');

// still bad
var subtract = Function('a', 'b', 'return a - b');
  • 7.11 関数シグネチャの空白。

なぜなら、一貫性は良いことですし、名前を追加または削除するときにスペースを追加または削除する必要がないからです。

// bad
const f = function(){};
const g = function (){};
const h = function() {};

// good
const x = function () {};
const y = function a() {};

なぜなら、パラメータとして渡されたオブジェクトを操作すると、呼び出し元で予期しない変数の副作用が発生する可能性があるからです。

// bad
function f1(obj) {
    obj.key = 1;
};

// good
function f2(obj) {
    const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
};

なぜなら、パラメータへの再代入は、特に arguments オブジェクトにアクセスする場合に予期しない動作を引き起こす可能性があるからです。また、特に V8 エンジンでは最適化の問題を引き起こす可能性もあります。

```javascript
// bad
function f1(a) {
a = 1;
}

function f2(a) {
if (!a) { a = 1; }
}

// good
function f3(a) {
const b = a || 1;
}

function f4(a = 1) {
}
```

アロー関数

なぜなら、これは this のコンテキストで実行されるバージョンの関数を作成し(通常はこれが望ましい動作です)、構文もより簡潔だからです。

なぜダメなのか? かなり複雑な関数の場合は、そのロジックを独自の関数宣言に移動した方がよいかもしれません。

// bad
[1, 2, 3].map(function (x) {
    const y = x + 1;
    return x * y;
});

// good
[1, 2, 3].map((x) => {
    const y = x + 1;
    return x * y;
});

なぜなら、シンタックスシュガーだからです。複数の関数を連鎖させる場合に読みやすくなります。

なぜダメなのか? オブジェクトを返す予定がある場合。

// bad
[1, 2, 3].map(number => {
    const nextNumber = number + 1;
    `A string containing the ${nextNumber}.`;
});

// good
[1, 2, 3].map(number => `A string containing the ${number}.`);

// good
[1, 2, 3].map((number) => {
    const nextNumber = number + 1;
    return `A string containing the ${nextNumber}.`;
});
  • 8.3 式が複数行にまたがる場合は、読みやすくするために括弧で囲んでください。

なぜなら、関数の開始と終了が明確になるからです。

// bad
[1, 2, 3].map(number => 'As time went by, the string containing the ' +
    `${number} became much longer. So we needed to break it over multiple ` +
    'lines.'
);

// good
[1, 2, 3].map(number => (
    `As time went by, the string containing the ${number} became much ` +
    'longer. So we needed to break it over multiple lines.'
));
  • 8.4 関数が単一の引数を取り、中括弧を使用しない場合は、括弧を省略してください。それ以外の場合は、常に引数を括弧で囲んでください。eslint: arrow-parens jscs: disallowParenthesesAroundArrowParam

なぜなら、見た目がすっきりするからです。

// bad
[1, 2, 3].map((x) => x * x);

// good
[1, 2, 3].map(x => x * x);

```javascript
// good
[1, 2, 3].map(number => (
    `A long string with the ${number}. Its so long that weve broken it ` +
    'over multiple lines!'
));

// bad
[1, 2, 3].map(x => {
    const y = x + 1;
    return x * y;
});

// good
[1, 2, 3].map((x) => {
    const y = x + 1;
    return x * y;
});
  • 8.5 アロー関数構文 (=>) と比較演算子 (<=, >=) を混同しないようにしてください。eslint: no-confusing-arrow
// bad
const itemHeight = item => item.height > 256 ? item.largeSize : item.smallSize;

// bad
const itemHeight = (item) => item.height > 256 ? item.largeSize : item.smallSize;

// good
const itemHeight = item => { return item.height > 256 ? item.largeSize : item.smallSize; }

クラス

  • 9.1 常に class を使用してください。prototype を直接操作することは避けてください。

なぜなら、class 構文の方が簡潔で、論理的に理解しやすいからです。

// bad
function Queue(contents = []) {
    this._queue = [...contents];
}
Queue.prototype.pop = function () {
    const value = this._queue[0];
    this._queue.splice(0, 1);
    return value;
}


// good
class Queue {
    constructor(contents = []) {
    this._queue = [...contents];
    }
    pop() {
    const value = this._queue[0];
    this._queue.splice(0, 1);
    return value;
    }
}
  • 9.2 継承には extends を使用してください。

なぜなら、これは instanceof を破壊することなくプロトタイプから機能を継承する組み込みの方法だからです。

// bad
const inherits = require('inherits');
function PeekableQueue(contents) {
    Queue.apply(this, contents);
}
inherits(PeekableQueue, Queue);
PeekableQueue.prototype.peek = function () {
    return this._queue[0];
}

// good
class PeekableQueue extends Queue {
    peek() {
    return this._queue[0];
    }
}
  • 9.3 メソッドチェーンを助けるために、メソッド内で this を返すことができます。
// bad
Jedi.prototype.jump = function () {
    this.jumping = true;
    return true;
};

Jedi.prototype.setHeight = function (height) {
    this.height = height;
};

const luke = new Jedi();
luke.jump(); // => true
luke.setHeight(20); // => undefined

// good
class Jedi {
    jump() {
    this.jumping = true;
    return this;
    }

    setHeight(height) {
    this.height = height;
    return this;
    }
}

const luke = new Jedi();

luke.jump()
    .setHeight(20);
  • 9.4 カスタムの toString() メソッドを作成しても構いませんが、正しく動作し、副作用がないことを確認してください。
class Jedi {
    constructor(options = {}) {
    this.name = options.name || 'no name';
    }

    getName() {
    return this.name;
    }

    toString() {
    return `Jedi - ${this.getName()}`;
    }
}
  • 9.5 指定されていない場合、クラスにはデフォルトのコンストラクタがあります。空のコンストラクタや、単に親クラスに委譲するだけのコンストラクタは不要です。no-useless-constructor
// bad
class Jedi {
    constructor() {}

    getName() {
    return this.name;
    }
}

// bad
class Rey extends Jedi {
    constructor(...args) {
    super(...args);
    }
}

// good
class Rey extends Jedi {
    constructor(...args) {
    super(...args);
    this.name = 'Rey';
    }
}

モジュール

  • 10.1 非標準のモジュールシステムよりも、常にモジュール (import/export) を使用してください。いつでも好みのモジュールシステムにトランスパイルできます。

なぜなら、モジュールは未来の標準であり、今から未来を使い始めるべきだからです。

// bad
const AirbnbStyleGuide = require('./AirbnbStyleGuide');
module.exports = AirbnbStyleGuide.es6;

// ok
import AirbnbStyleGuide from './AirbnbStyleGuide';
export default AirbnbStyleGuide.es6;

// best
import { es6 } from './AirbnbStyleGuide';
export default es6;
  • 10.2 ワイルドカードインポートは使用しないでください。

なぜなら、これにより単一のデフォルトエクスポートを持つことが保証されるからです。

// bad
import * as AirbnbStyleGuide from './AirbnbStyleGuide';

// good
import AirbnbStyleGuide from './AirbnbStyleGuide';
  • 10.3 また、インポートから直接エクスポートしないでください。

なぜなら、一行で書けるのは簡潔ですが、インポートとエクスポートを明確に分けることで一貫性が保たれるからです。

// bad
// filename es6.js
export { es6 as default } from './airbnbStyleGuide';

// good
// filename es6.js
import { es6 } from './AirbnbStyleGuide';
export default es6;

イテレータとジェネレータ

  • 11.1 イテレータを使用しないでください。for-of ループの代わりに、map()reduce() のような JavaScript の高階関数を使用してください。eslint: no-iterator

なぜなら、これは不変性のルールを強制するためです。副作用を扱うよりも、値を返す純粋関数を扱う方が論理的に理解しやすいからです。

eslint rules: no-iterator.

const numbers = [1, 2, 3, 4, 5];

// bad
let sum = 0;
for (let num of numbers) {
    sum += num;
}

sum === 15;

// good
let sum = 0;
numbers.forEach(num => sum += num);
sum === 15;

// best (use the functional force)
const sum = numbers.reduce((total, num) => total + num, 0);
sum === 15;
  • 11.2 ジェネレータは今のところ使用しないでください。

なぜなら、まだ ES5 へのトランスパイルが完全ではないからです。

プロパティ

const luke = {
    jedi: true,
    age: 28,
};

// bad
const isJedi = luke['jedi'];

// good
const isJedi = luke.jedi;
  • 12.2 変数を使ってプロパティにアクセスするときはブラケット記法 [] を使用してください。
const luke = {
    jedi: true,
    age: 28,
};

function getProp(prop) {
    return luke[prop];
}

const isJedi = getProp('jedi');

変数

  • 13.1 変数を宣言するときは常に const または let を使用してください。そうしないとグローバル変数になってしまいます。グローバル名前空間の汚染は避けたいものです。キャプテン・プラネットもそう警告していました。
// bad
superPower = new SuperPower();

// good
const superPower = new SuperPower();

なぜなら、この方が新しい変数の宣言を追加しやすく、;, に置き換えたり、句読点だけの差分が発生したりする心配がないからです。

// bad
const items = getItems(),
    goSportsTeam = true,
    dragonball = 'z';

// bad
// (compare to above, notice the error)
const items = getItems(),
    goSportsTeam = true;
    dragonball = 'z';

// good
const items = getItems();
const goSportsTeam = true;
const dragonball = 'z';
  • 13.3 すべての const をグループ化し、次にすべての let をグループ化してください。

なぜなら、後になって以前に割り当てられた変数に基づいて変数を割り当てる必要がある場合に役立つからです。

// bad
let i, len, dragonball,
    items = getItems(),
    goSportsTeam = true;

// bad
let i;
const items = getItems();
let dragonball;
const goSportsTeam = true;
let len;

// good
const goSportsTeam = true;
const items = getItems();
let dragonball;
let i;
let length;
  • 13.4 変数は必要な場所で割り当てますが、妥当な場所に配置してください。

なぜなら、letconst は関数スコープではなくブロックスコープだからです。

// bad - unnecessary function call
function checkName(hasName) {
    const name = getName();

    if (hasName === 'test') {
    return false;
    }

    if (name === 'test') {
    this.setName('');
    return false;
    }

    return name;
}

// good
function checkName(hasName) {
    if (hasName === 'test') {
    return false;
    }

    const name = getName();

    if (name === 'test') {
    this.setName('');
    return false;
    }

    return name;
}

ホイスティング

// we know this wouldn't work (assuming there
// is no notDefined global variable)
function example() {
    console.log(notDefined); // => throws a ReferenceError
}

// creating a variable declaration after you
// reference the variable will work due to
// variable hoisting. Note: the assignment
// value of `true` is not hoisted.
function example() {
    console.log(declaredButNotAssigned); // => undefined
    var declaredButNotAssigned = true;
}

// The interpreter is hoisting the variable
// declaration to the top of the scope,
// which means our example could be rewritten as:
function example() {
    let declaredButNotAssigned;
    console.log(declaredButNotAssigned); // => undefined
    declaredButNotAssigned = true;
}

// using const and let
function example() {
    console.log(declaredButNotAssigned); // => throws a ReferenceError
    console.log(typeof declaredButNotAssigned); // => throws a ReferenceError
    const declaredButNotAssigned = true;
}
  • 14.2 匿名関数式は変数名を巻き上げますが、関数の代入は巻き上げません。
function example() {
    console.log(anonymous); // => undefined

    anonymous(); // => TypeError anonymous is not a function

    var anonymous = function () {
    console.log('anonymous function expression');
    };
}
  • 14.3 名前付き関数式は変数名を巻き上げますが、関数名や関数本体は巻き上げません。
function example() {
    console.log(named); // => undefined

    named(); // => TypeError named is not a function

    superPower(); // => ReferenceError superPower is not defined

    var named = function superPower() {
    console.log('Flying');
    };
}

// the same is true when the function name
// is the same as the variable name.
function example() {
    console.log(named); // => undefined

    named(); // => TypeError named is not a function

    var named = function named() {
    console.log('named');
    }
}
  • 14.4 関数宣言は、その名前と関数本体を巻き上げます。
function example() {
    superPower(); // => Flying

    function superPower() {
    console.log('Flying');
    }
}

比較演算子と等価性

  • 15.1 ==!= よりも ===!== を使用してください。eslint: eqeqeq

  • 15.2 if 文などの条件文は、ToBoolean 抽象メソッドによる強制型変換を使用して式を評価し、常に以下の単純なルールに従います:

  • オブジェクトtrue と評価されます
  • Undefinedfalse と評価されます
  • Nullfalse と評価されます
  • ブール値そのブール値 と評価されます
  • 数値+0, -0, NaN の場合は false、それ以外は true と評価されます
  • 文字列 は空文字 '' の場合は false、それ以外は true と評価されます
if ([0] && []) {
    // true
    // an array (even an empty one) is an object, objects will evaluate to true
}
  • 15.3 ショートカットを使用してください。
// bad
if (name !== '') {
    // ...stuff...
}

// good
if (name) {
    // ...stuff...
}

// bad
if (collection.length > 0) {
    // ...stuff...
}

// good
if (collection.length) {
    // ...stuff...
}
  • 15.4 詳細については、Angus Croll による Truth Equality and JavaScript を参照してください。
  • 15.5 レキシカル宣言(letconstfunctionclass など)を含む casedefault 節では、ブロックを作成するために中括弧を使用してください。

なぜなら、レキシカル宣言は switch ブロック全体で可視ですが、初期化されるのは割り当て時のみであり、それはその case に到達したときだけだからです。複数の case 節が同じものを定義しようとすると問題が発生します。

eslint rules: no-case-declarations.

// bad
switch (foo) {
    case 1:
    let x = 1;
    break;
    case 2:
    const y = 2;
    break;
    case 3:
    function f() {}
    break;
    default:
    class C {}
}

// good
switch (foo) {
    case 1: {
    let x = 1;
    break;
    }
    case 2: {
    const y = 2;
    break;
    }
    case 3: {
    function f() {}
    break;
    }
    case 4:
    bar();
    break;
    default: {
    class C {}
    }
}
  • 15.7 三項演算子はネストすべきではなく、通常は1行の式にするべきです。

eslint rules: no-nested-ternary.

// bad
const foo = maybe1 > maybe2
    ? "bar"
    : value1 > value2 ? "baz" : null;

// better
const maybeNull = value1 > value2 ? 'baz' : null;

const foo = maybe1 > maybe2
    ? 'bar'
    : maybeNull;

// best
const maybeNull = value1 > value2 ? 'baz' : null;

const foo = maybe1 > maybe2 ? 'bar' : maybeNull;
  • 15.8 不要な三項演算子は避けてください。

eslint rules: no-unneeded-ternary.

// bad
const foo = a ? a : b;
const bar = c ? true : false;
const baz = c ? false : true;

// good
const foo = a || b;
const bar = !!c;
const baz = !c;

ブロック

  • 16.1 すべての複数行ブロックには中括弧を使用してください。
// bad
if (test)
    return false;

// good
if (test) return false;

// good
if (test) {
    return false;
}

// bad
function foo() { return false; }

// good
function bar() {
    return false;
}
// bad
if (test) {
    thing1();
    thing2();
}
else {
    thing3();
}

// good
if (test) {
    thing1();
    thing2();
} else {
    thing3();
}

制御文

  • 17.1 制御文(if, while など)が長くなりすぎたり、最大行長を超える場合は、各(グループ化された)条件を新しい行に置くことができます。論理演算子は行の先頭に置くべきです。

なぜなら、演算子を行頭に置くことで演算子の位置が揃い、メソッドチェーンと同様のパターンに従うことができるからです。また、複雑なロジックを視覚的に把握しやすくなり、可読性が向上します。

// bad
if ((foo === 123 || bar === 'abc') && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening()) {
    thing1();
}

// bad
if (foo === 123 &&
    bar === 'abc') {
    thing1();
}

// bad
if (foo === 123
    && bar === 'abc') {
    thing1();
}

// bad
if (
    foo === 123 &&
    bar === 'abc'
) {
    thing1();
}

// good
if (
    foo === 123
    && bar === 'abc'
) {
    thing1();
}

// good
if (
    (foo === 123 || bar === 'abc')
    && doesItLookGoodWhenItBecomesThatLong()
    && isThisReallyHappening()
) {
    thing1();
}

// good
if (foo === 123 && bar === 'abc') {
    thing1();
}
  • 17.2 制御文の代わりに選択演算子を使用しないでください。
// bad
!isRunning && startRunning();

// good
if (!isRunning) {
    startRunning();
}

コメント

  • 18.1 複数行のコメントには /** ... */ を使用してください。説明を含め、すべてのパラメータと戻り値の型と値を指定してください。
// bad
// make() returns a new element
// based on the passed in tag name
//
// @param {String} tag
// @return {Element} element
function make(tag) {

    // ...stuff...

    return element;
}

// good
/**
 * make() returns a new element
 * based on the passed in tag name
 *
 * @param {String} tag
 * @return {Element} element
 */
function make(tag) {

    // ...stuff...

    return element;
}
  • 18.2 1行コメントには // を使用してください。1行コメントは、コメントの対象となる行の上に新しい行で配置してください。ブロックの最初の行でない限り、コメントの前には空行を入れてください。
// bad
const active = true;  // is current tab

// good
// is current tab
const active = true;

// bad
function getType() {
    console.log('fetching type...');
    // set the default type to 'no type'
    const type = this._type || 'no type';

    return type;
}

// good
function getType() {
    console.log('fetching type...');

    // set the default type to 'no type'
    const type = this._type || 'no type';

    return type;
}

// also good
function getType() {
    // set the default type to 'no type'
    const type = this._type || 'no type';

    return type;
}
  • 18.3 コメントの先頭に FIXMETODO を付けると、他の開発者が、再検討が必要な問題を指摘しているのか、実装が必要な解決策を提案しているのかを素早く理解するのに役立ちます。これらは通常のコメントとは異なり、実行可能(actionable)です。アクションは FIXME: -- これを解決する必要がある または TODO: -- これを実装する必要がある です。

  • 18.4 問題を注釈するには // FIXME: を使用してください。

class Calculator extends Abacus {
    constructor() {
    super();

    // FIXME: shouldn't use a global here
    total = 0;
    }
}
  • 18.5 問題の解決策を注釈するには // TODO: を使用してください。
class Calculator extends Abacus {
    constructor() {
    super();

    // TODO: total should be configurable by an options param
    this.total = 0;
    }
}

空白

// bad
function foo() {
∙∙∙∙const name;
}

// bad
function bar() {
const name;
}

// good
function baz() {
∙∙const name;
}
// bad
function test(){
    console.log('test');
}

// good
function test() {
    console.log('test');
}

// bad
dog.set('attr',{
    age: '1 year',
    breed: 'Bernese Mountain Dog',
});

// good
dog.set('attr', {
    age: '1 year',
    breed: 'Bernese Mountain Dog',
});
// bad
if(isJedi) {
    fight ();
}

// good
if (isJedi) {
    fight();
}

// bad
function fight () {
    console.log ('Swooosh!');
}

// good
function fight() {
    console.log('Swooosh!');
}
// bad
const x=y+5;

// good
const x = y + 5;
  • 19.5 ファイルの末尾は単一の改行文字で終了してください。
// bad
(function (global) {
    // ...stuff...
})(this);
// bad
(function (global) {
    // ...stuff...
})(this);

// good
(function (global) {
    // ...stuff...
})(this);
  • 19.6 長いメソッドチェーン(2つ以上のメソッドチェーン)を作成する場合は、インデントを使用してください。先頭にドットを使用することで、その行が新しい文ではなくメソッド呼び出しであることを強調します。eslint: newline-per-chained-call no-whitespace-before-property
// bad
$('#items').find('.selected').highlight().end().find('.open').updateCount();

// bad
$('#items').
    find('.selected').
    highlight().
    end().
    find('.open').
    updateCount();

// good
$('#items')
    .find('.selected')
    .highlight()
    .end()
    .find('.open')
    .updateCount();

// bad
const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true)
    .attr('width', (radius + margin) * 2).append('svg:g')
    .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
    .call(tron.led);

// good
const leds = stage.selectAll('.led')
    .data(data)
    .enter().append('svg:svg')
    .classed('led', true)
    .attr('width', (radius + margin) * 2)
    .append('svg:g')
    .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
    .call(tron.led);

// good
const leds = stage.selectAll('.led').data(data);
// bad
if (foo) {
    return bar;
}
return baz;

// good
if (foo) {
    return bar;
}

return baz;

// bad
const obj = {
    foo() {
    },
    bar() {
    },
};
return obj;

// good
const obj = {
    foo() {
    },

    bar() {
    },
};

return obj;

// bad
const arr = [
    function foo() {
    },
    function bar() {
    },
];
return arr;

// good
const arr = [
    function foo() {
    },

    function bar() {
    },
];

return arr;
// bad
function bar() {

    console.log(foo);

}

// also bad
if (baz) {

    console.log(qux);
} else {
    console.log(foo);

}

// good
function bar() {
    console.log(foo);
}

// good
if (baz) {
    console.log(qux);
} else {
    console.log(foo);
}
// bad
function bar( foo ) {
    return foo;
}

// good
function bar(foo) {
    return foo;
}

// bad
if ( foo ) {
    console.log(foo);
}

// good
if (foo) {
    console.log(foo);
}
// bad
const foo = [ 1, 2, 3 ];
console.log(foo[ 0 ]);

// good
const foo = [1, 2, 3];
console.log(foo[0]);
// bad
const foo = {clark: 'kent'};

// good
const foo = { clark: 'kent' };

なぜなら、可読性と保守性が確保されるからです。

// bad
const foo = 'Whatever national crop flips the window. The cartoon reverts within the screw. Whatever wizard constrains a helpful ally. The counterpart ascends!';

// bad
$.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.'));

// good
const foo = 'Whatever national crop flips the window. The cartoon reverts within the screw. ' +
    'Whatever wizard constrains a helpful ally. The counterpart ascends!';

// good
$.ajax({
    method: 'POST',
    url: 'https://airbnb.com/',
    data: { name: 'John' },
})
    .done(() => console.log('Congratulations!'))
    .fail(() => console.log('You have failed this city.'));

カンマ

// bad
const story = [
    once
    , upon
    , aTime
];

// good
const story = [
    once,
    upon,
    aTime,
];

// bad
const hero = {
    firstName: 'Ada'
    , lastName: 'Lovelace'
    , birthYear: 1815
    , superPower: 'computers'
};

// good
const hero = {
    firstName: 'Ada',
    lastName: 'Lovelace',
    birthYear: 1815,
    superPower: 'computers',
};

なぜなら、これにより Git の差分がきれいになるからです。また、Babel などのトランスパイラはトランスパイル後のコードで余分な末尾のカンマを削除するため、レガシーブラウザにおける 末尾のカンマの問題 を心配する必要がありません。

// bad - git diff without trailing comma
const hero = {
        firstName: 'Florence',
    lastName: 'Nightingale'
+    lastName: 'Nightingale',
+    inventorOf: ['coxcomb graph', 'modern nursing']
};

// good - git diff with trailing comma
const hero = {
        firstName: 'Florence',
        lastName: 'Nightingale',
+    inventorOf: ['coxcomb chart', 'modern nursing'],
};

// bad
const hero = {
    firstName: 'Dana',
    lastName: 'Scully'
};

const heroes = [
    'Batman',
    'Superman'
];

// good
const hero = {
    firstName: 'Dana',
    lastName: 'Scully',
};

const heroes = [
    'Batman',
    'Superman',
];

セミコロン

// bad
(function () {
    const name = 'Skywalker'
    return name
})()

// good
(() => {
    const name = 'Skywalker';
    return name;
}());

// good (guards against the function becoming an argument when two files with IIFEs are concatenated)
;(() => {
    const name = 'Skywalker';
    return name;
}());

詳細はこちら

型変換と強制

  • 22.1 文の先頭で型強制を行ってください。
  • 22.2 文字列:
// => this.reviewScore = 9;

// bad
const totalScore = this.reviewScore + '';

// good
const totalScore = String(this.reviewScore);
  • 22.3 数値: 型キャストには Number を使用し、文字列の解析には常に基数(radix)を指定して parseInt を使用してください。eslint: radix
const inputValue = '4';

// bad
const val = new Number(inputValue);

// bad
const val = +inputValue;

// bad
const val = inputValue >> 0;

// bad
const val = parseInt(inputValue);

// good
const val = Number(inputValue);

// good
const val = parseInt(inputValue, 10);
  • 22.4 何らかの理由でクレイジーなことをしていて parseInt がボトルネックになっており、パフォーマンス上の理由 でビットシフトを使用する必要がある場合は、その理由と行っている内容を説明するコメントを残してください。
// good
/**
 * parseInt was the reason my code was slow.
 * Bitshifting the String to coerce it to a
 * Number made it a lot faster.
 */
const val = inputValue >> 0;
  • 22.5 注意: ビットシフト操作を使用する場合は注意してください。数値は 64ビット値 として表現されますが、ビットシフト操作は常に32ビット整数を返します (source)。ビットシフトは、32ビットを超える整数値に対して予期しない動作を引き起こす可能性があります。Discussion。符号付き32ビット整数の最大値は 2,147,483,647 です:
2147483647 >> 0 //=> 2147483647
2147483648 >> 0 //=> -2147483648
2147483649 >> 0 //=> -2147483647
  • 22.6 ブール値:
const age = 0;

// bad
const hasAge = new Boolean(age);

// good
const hasAge = Boolean(age);

// good
const hasAge = !!age;

命名規則

  • 23.1 1文字の名前は避けてください。名前は説明的にしてください。
// bad
function q() {
    // ...stuff...
}

// good
function query() {
    // ..stuff..
}
// bad
const OBJEcttsssss = {};
const this_is_my_object = {};
function c() {}

// good
const thisIsMyObject = {};
function thisIsMyFunction() {}
// bad
function user(options) {
    this.name = options.name;
}

const bad = new user({
    name: 'nope',
});

// good
class User {
    constructor(options) {
    this.name = options.name;
    }
}

const good = new User({
    name: 'yup',
});
// bad
this.__firstName__ = 'Panda';
this.firstName_ = 'Panda';

// good
this._firstName = 'Panda';
  • 23.5 this への参照を保存しないでください。アロー関数または Function#bind を使用してください。jscs: disallowNodeTypes
// bad
function foo() {
    const self = this;
    return function () {
    console.log(self);
    };
}

// bad
function foo() {
    const that = this;
    return function () {
    console.log(that);
    };
}

// good
function foo() {
    return () => {
    console.log(this);
    };
}
  • 23.6 ファイルが単一のクラスをエクスポートする場合は、ファイル名はクラス名と完全に一致させてください。
// file contents
class CheckBox {
    // ...
}
export default CheckBox;

// in some other file
// bad
import CheckBox from './checkBox';

// bad
import CheckBox from './check_box';

// good
import CheckBox from './CheckBox';
  • 23.7 デフォルトの関数をエクスポートする場合はキャメルケースを使用してください。ファイル名は関数名と同じにしてください。
function makeStyleGuide() {
}

export default makeStyleGuide;
  • 23.8 シングルトン / 関数ライブラリ / 空のオブジェクトをエクスポートする場合はパスカルケースを使用してください。
const AirbnbStyleGuide = {
    es6: {
    }
};

export default AirbnbStyleGuide;

アクセサ

  • 24.1 プロパティのアクセサ関数は必須ではありません。
  • 24.2 JavaScript のゲッター/セッターは使用しないでください。これらは予期しない副作用を引き起こし、テストや保守、推論が難しくなるためです。代わりに、アクセサ関数を作成する場合は、getVal() や setVal(‘hello’) を使用してください。
// bad
dragon.age();

// good
dragon.getAge();

// bad
dragon.age(25);

// good
dragon.setAge(25);
  • 24.3 プロパティがブール値の場合は、isVal() または hasVal() を使用してください。
// bad
if (!dragon.age()) {
    return false;
}

// good
if (!dragon.hasAge()) {
    return false;
}
  • 24.4 get() および set() 関数を作成しても構いませんが、一貫性を持たせてください。
class Jedi {
    constructor(options = {}) {
    const lightsaber = options.lightsaber || 'blue';
    this.set('lightsaber', lightsaber);
    }

    set(key, val) {
    this[key] = val;
    }

    get(key) {
    return this[key];
    }
}

イベント

  • 25.1 イベントにデータペイロードを添付する場合(DOM イベントであれ、Backbone イベントのような独自のイベントであれ)、生の値ではなくハッシュ(オブジェクト)を渡してください。これにより、後の貢献者がイベントのすべてのハンドラを見つけて更新することなく、イベントペイロードにデータを追加できます。例えば、次のような書き方ではなく:
// bad
$(this).trigger('listingUpdated', listing.id);

...

$(this).on('listingUpdated', (e, listingId) => {
    // do something with listingId
});

次のように書いてください:

// good
$(this).trigger('listingUpdated', { listingId: listing.id });

...

$(this).on('listingUpdated', (e, data) => {
    // do something with data.listingId
});

jQuery

// bad
const sidebar = $('.sidebar');

// good
const $sidebar = $('.sidebar');

// good
const $sidebarBtn = $('.sidebar-btn');
  • 26.2 jQuery の検索結果をキャッシュしてください。
// bad
function setSidebar() {
    $('.sidebar').hide();

    // ...stuff...

    $('.sidebar').css({
    'background-color': 'pink'
    });
}

// good
function setSidebar() {
    const $sidebar = $('.sidebar');
    $sidebar.hide();

    // ...stuff...

    $sidebar.css({
    'background-color': 'pink'
    });
}
  • 26.3 DOM クエリには、カスケード $('.sidebar ul') または 親 > 子 $('.sidebar > ul') を使用してください。jsPerf
  • 26.4 スコープ付き jQuery オブジェクトクエリで find を使用してください。
// bad
$('ul', '.sidebar').hide();

// bad
$('.sidebar').find('ul').hide();

// good
$('.sidebar ul').hide();

// good
$('.sidebar > ul').hide();

// good
$sidebar.find('ul').hide();

ECMAscript 5 互換性

ECMAScript 6 スタイル

  • 28.1 以下は、様々な ES6 機能へのリンク集です。
  1. アロー関数
  2. クラス
  3. オブジェクトの短縮表記
  4. オブジェクトの簡潔表記
  5. 計算されたオブジェクトプロパティ
  6. テンプレート文字列
  7. 分割代入
  8. デフォルトパラメータ
  9. Rest
  10. 配列スプレッド
  11. Let と Const
  12. イテレータとジェネレータ
  13. モジュール

標準ライブラリ

標準ライブラリ には、機能が壊れているものの、レガシーな理由で残されている機能的ユーティリティがいくつか含まれています。

  • 29.1 isNaN の代わりに Number.isNaN を使用してください。

なぜなら、isNaN は非数値を数値に強制変換し、NaN に変換されるものすべてに対して true を返すからです。 もしこの動作が望ましい場合は、明示的に行ってください。

// bad
isNaN('1.2'); // false
isNaN('1.2.3'); // true

// good
Number.isNaN('1.2.3'); // false
Number.isNaN(Number('1.2.3')); // true
  • 29.2 isFinite の代わりに Number.isFinite を使用してください。

なぜなら、isFinite は非数値を数値に強制変換し、有限数に変換されるものすべてに対して true を返すからです。 もしこの動作が望ましい場合は、明示的に行ってください。

// bad
isFinite('2e3'); // true

// good
Number.isFinite('2e3'); // false
Number.isFinite(parseInt('2e3', 10)); // true

テスト

function foo() {
    return true;
}
  • 30.2 冗談はさておき:
  • どのテストフレームワークを使用するにしても、テストを書くべきです!
  • 多くの小さな純粋関数を書くように努め、変更(mutation)が発生する場所を最小限に抑えてください。
  • スタブやモックには注意してください - テストが脆くなる可能性があります。
  • Airbnb では主に mocha を使用しています。tape も小さな独立したモジュールで時々使用されます。
  • 100% のテストカバレッジを目指すことは良い目標ですが、常にそれが現実的であるとは限りません。
  • バグを修正するたびに、回帰テストを書いてください。回帰テストなしで修正されたバグは、将来ほぼ確実に再び発生します。

パフォーマンス

リソース

Learning ES6

Read This

Tools

  • Code Style Linters

Other Style Guides

Other Styles

Further Reading

Books

Blogs

Podcasts

採用企業

これは、このスタイルガイドを使用している企業のリストです。もし使用している場合は、プルリクエストを送って追加してください。

翻訳

このスタイルガイドは他の言語でも利用可能です:

JavaScript スタイルガイドガイド

JavaScript についてチャットする

コントリビューター

License

(The MIT License)

Copyright (c) 2014-2016 Airbnb

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ‘Software’), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Amendments

We encourage you to fork this guide and change the rules to fit your team’s style guide. Below, you may list some amendments to the style guide. This allows you to periodically update your style guide without having to deal with merge conflicts.

Reference

All rights reserved,未經允許不得隨意轉載
Built with Hugo
テーマ StackJimmy によって設計されています。