Featured image of post Руководство по стилю JavaScript от Airbnb

Руководство по стилю JavaScript от Airbnb

Руководство по стилю JavaScript от Airbnb

Photo by Pankaj Patel on Unsplash

https://github.com/jigsawye/javascript

Руководство по стилю JavaScript от Airbnb()

Разумный подход к JavaScript.

Downloads Gitter

Другие руководства по стилю

Переведено из Airbnb JavaScript Style Guide.

Типы (Types)

  • 1.1 Примитивы (Primitives): Вы обращаетесь к примитивным типам напрямую.
  • string
  • number
  • boolean
  • null
  • undefined
const foo = 1;
let bar = foo;

bar = 9;

console.log(foo, bar); // => 1, 9
  • 1.2 Сложные (Complex): Вы обращаетесь к сложным типам по ссылке (by reference).
  • object
  • array
  • function
const foo = [1, 2];
const bar = foo;

bar[0] = 9;

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

Ссылки (References)

  • 2.1 Используйте const для всех ваших ссылок; избегайте использования var. eslint: prefer-const, no-const-assign

Почему? Это гарантирует, что вы не сможете переназначить свои ссылки, что может привести к ошибкам и трудночитаемому коду.

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

// good
const a = 1;
const b = 2;
  • 2.2 Если вам нужно переназначить ссылки, используйте let вместо var. eslint: no-var jscs: disallowVar

Почему? let имеет блочную область видимости (block-scoped), в то время как var имеет функциональную область видимости (function-scoped).

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

// good, use the let.
let count = 1;
if (true) {
    count += 1;
}
  • 2.3 Обратите внимание, что let и const имеют блочную область видимости.
// const и let существуют только внутри блоков, где они определены.
{
    let a = 1;
    const b = 1;
}
console.log(a); // ReferenceError
console.log(b); // ReferenceError

Объекты (Objects)

  • 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,
};
  • 3.3 Используйте читаемые синонимы вместо зарезервированных слов. jscs: disallowIdentifierNames
// bad
const superman = {
    class: 'alien',
};

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

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

  • 3.4 Используйте вычисляемые имена свойств (computed property names) при создании объектов с динамическими именами свойств.

Почему? Они позволяют определить все свойства объекта в одном месте.


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,
};
  • 3.8 Ставьте кавычки только вокруг свойств, которые являются недопустимыми идентификаторами. eslint: quote-props jscs: disallowQuotedKeysInObjects

Почему? В целом мы считаем это субъективно более легким для чтения. Это улучшает подсветку синтаксиса, а также легче оптимизируется многими движками JS.

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

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

Массивы (Arrays)

  • 4.1 Используйте литеральный синтаксис для создания массивов. eslint: no-array-constructor
// 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-like object) в массив, используйте Array#from.
const foo = document.querySelectorAll('.foo');
const nodes = Array.from(foo);
  • 4.5 Используйте инструкции возврата в колбэках методов массива. Допустимо опустить return, если тело функции состоит из одной инструкции, следующей за 8.2. 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;
});

Деструктуризация (Destructuring)

  • 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 Используйте деструктуризацию объекта для нескольких возвращаемых значений, а не деструктуризацию массива.

Почему? Вы можете добавлять новые свойства или изменять порядок вещей, не нарушая места вызова (call sites).

// 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);

Строки (Strings)

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

// good
const name = 'Capt. Janeway';
  • 6.2 Строки длиной более 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() для строки, это открывает слишком много уязвимостей.

Функции (Functions)

  • 7.1 Используйте объявления функций (function declarations) вместо функциональных выражений (function expressions). jscs: requireFunctionDeclarations

Почему? Объявления функций именуются, поэтому их легче идентифицировать в стеке вызовов. Кроме того, объявления функций поднимаются (hoisted), тогда как функциональные выражения - нет. Это правило позволяет Стрелочным функциям полностью заменить функциональные выражения.

// 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) {
}
```

Стрелочные функции (Arrow Functions)

  • 8.1 Когда вам нужно использовать функциональное выражение (например, при передаче анонимной функции), используйте запись стрелочной функции. eslint: prefer-arrow-callback, arrow-spacing jscs: requireArrowFunctions

Почему? Она создает версию функции, которая выполняется в контексте 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;
});
  • 8.2 Если тело функции состоит из одного выражения, возвращающего выражение без побочных эффектов, опустите фигурные скобки и используйте неявный возврат. В противном случае сохраните фигурные скобки и используйте оператор return. eslint: arrow-parens, arrow-body-style jscs: disallowParenthesesAroundArrowParam, requireShorthandArrowFunctions

Почему? Синтаксический сахар. Это читается лучше, когда несколько функций соединены в цепочку.

Почему нет? Если вы планируете вернуть объект.

// 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);
// good
[1, 2, 3].map(number => (
    `A long string with the ${number}. It’s so long that we’ve 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; }

Конструкторы (Constructors)

  • 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, чтобы помочь с цепочкой методов (method chaining).
// 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';
    }
}

Модули (Modules)

  • 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 Не используйте импорт с подстановочными знаками (wildcard imports).

Почему? Это гарантирует, что у вас есть один экспорт по умолчанию.

// 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;

Итераторы и генераторы (Iterators and Generators)

  • 11.1 Не используйте итераторы. Отдавайте предпочтение функциям высшего порядка JavaScript, таким как map() и reduce(), вместо циклов, таких как for-of. 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.

Свойства (Properties)

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');

Переменные (Variables)

  • 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 Присваивайте переменные там, где они вам нужны, но размещайте их в разумном месте.

Почему? let и const имеют блочную область видимости (block scoped), а не функциональную (function scoped).

// 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;
}

Поднятие (Hoisting)

// мы знаем, что это не сработает (предполагая, что
// нет глобальной переменной notDefined)
function example() {
    console.log(notDefined); // => throws a ReferenceError
}

// создание объявления переменной после того, как вы
// ссылаетесь на переменную, будет работать из-за
// поднятия переменной. Примечание: присвоение
// значения `true` не поднимается.
function example() {
    console.log(declaredButNotAssigned); // => undefined
    var declaredButNotAssigned = true;
}

// Интерпретатор поднимает объявление переменной
// наверх области видимости,
// что означает, что наш пример может быть переписан как:
function example() {
    let declaredButNotAssigned;
    console.log(declaredButNotAssigned); // => undefined
    declaredButNotAssigned = true;
}

// использование const и 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');
    };
}

// то же самое верно, когда имя функции
// совпадает с именем переменной.
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');
    }
}

Операторы сравнения и равенства (Comparison Operators & Equality)

  • 15.1 Используйте === и !== вместо == и !=. eslint: eqeqeq

  • 15.2 Условные операторы, такие как if, оценивают свое выражение, используя приведение с помощью абстрактного метода ToBoolean, и всегда следуют этим простым правилам:

  • Objects оцениваются как true
  • Undefined оценивается как false
  • Null оценивается как false
  • Booleans оцениваются как значение булева
  • Numbers оцениваются как false, если +0, -0 или NaN, иначе true
  • Strings оцениваются как false, если пустая строка '', иначе true
if ([0] && []) {
    // true
    // массив (даже пустой) - это объект, объекты оцениваются как true
}
  • 15.3 Используйте сокращения.
// bad
if (name !== '') {
    // ...stuff...
}

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

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

// good
if (collection.length) {
    // ...stuff...
}
  • 15.4 Для получения дополнительной информации см. Truth Equality and JavaScript от Angus Croll.
  • 15.5 Используйте фигурные скобки для создания блоков в операторах case и default, которые содержат лексические объявления (например, let, const, function и class).

Почему? Лексические объявления видны во всем блоке 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 Тернарные операторы не должны быть вложенными и, как правило, должны быть однострочными выражениями.

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;

Блоки (Blocks)

  • 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;
}
  • 16.2 Если вы используете многострочные блоки с if и else, поместите else на ту же строку, что и закрывающая фигурная скобка блока if. eslint: brace-style jscs: disallowNewlineBeforeBlockStatements
// bad
if (test) {
    thing1();
    thing2();
}
else {
    thing3();
}

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

Управляющие конструкции (Control Statements)

  • 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();
}

Комментарии (Comments)

  • 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 Используйте // для однострочных комментариев. Помещайте однострочные комментарии на новой строке над темой комментария. Оставьте пустую строку перед комментарием, если он не находится в первой строке блока.
// 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 Использование префиксов FIXME или TODO в комментариях помогает другим разработчикам быстро понять, указываете ли вы на проблему, которую необходимо пересмотреть, или предлагаете решение, которое необходимо реализовать. Они отличаются от обычных комментариев тем, что они требуют действий. Действия: FIXME: -- need to figure this out или TODO: -- need to implement.

  • 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;
    }
}

Пробелы (Whitespace)

  • 19.1 Используйте мягкие табы (пробелы), установленные на 2 пробела. eslint: indent jscs: validateIndentation
// 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',
});
  • 19.3 Поставьте 1 пробел перед открывающей скобкой в управляющих конструкциях (if, while и т.д.). Не ставьте пробел между списком аргументов и именем функции в вызовах и объявлениях функций. eslint: space-after-keywords, space-before-keywords jscs: requireSpaceAfterKeywords
// 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' };
  • 19.12 Избегайте строк длиннее 100 символов (включая пробелы). eslint: max-len jscs: maximumLineLength

Почему? Это обеспечивает читаемость и удобство сопровождения.

// 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.'));

Запятые (Commas)

// 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 diff. Кроме того, транспиляторы, такие как 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',
];

Точки с запятой (Semicolons)

// 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;
}());

Читать дальше.

Приведение типов и коэрцитивность (Type Casting & Coercion)

  • 22.1 Выполняйте приведение типов в начале инструкции.
  • 22.2 Строки:
// => this.reviewScore = 9;

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

// good
const totalScore = String(this.reviewScore);
  • 22.3 Числа: Используйте Number для приведения типов и 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 является вашим узким местом, и вам нужно использовать Bitshift для соображений производительности, оставьте комментарий, объясняющий, почему и что вы делаете.
// 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 Note: Будьте осторожны при использовании операций битового сдвига. Числа представлены как 64-битные значения, но операции битового сдвига всегда возвращают 32-битное целое число (источник). Битовый сдвиг может привести к неожиданному поведению для целочисленных значений больше 32 бит. Обсуждение. Самое большое 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;

Соглашения об именовании (Naming Conventions)

  • 23.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 Используйте camelCase при экспорте функции по умолчанию. Имя вашего файла должно быть идентичным имени вашей функции.
function makeStyleGuide() {
}

export default makeStyleGuide;
  • 23.8 Используйте PascalCase при экспорте синглтона / библиотеки функций / пустого объекта.
const AirbnbStyleGuide = {
    es6: {
    }
};

export default AirbnbStyleGuide;

Аксессоры (Accessors)

  • 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];
    }
}

События (Events)

  • 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') или parent > child $('.sidebar > ul'). jsPerf
  • 26.4 Используйте find с запросами к объекту jQuery с областью видимости.
// 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 5 Compatibility)

Стили ECMAScript 6 (ECMAScript 6 Styles)

  • 28.1 Это коллекция ссылок на различные функции ES6.
  1. Arrow Functions
  2. Classes
  3. Object Shorthand
  4. Object Concise
  5. Computed Object Properties
  6. Template Strings
  7. Destructuring
  8. Default Parameters
  9. Rest
  10. Array Spreads
  11. Let and Const
  12. Iterators and Generators
  13. Modules

Стандартная библиотека (Standard Library)

Стандартная библиотека содержит утилиты, которые сломаны, но сохранены по соображениям наследия.

  • 29.1 Используйте Number.isNaN вместо isNaN.

Почему? isNaN приводит нечисловые значения к числам, возвращая true для всего, что приводится к NaN. Если такое поведение желательно, сделайте его явным.

// 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 Используйте Number.isFinite вместо isFinite.

Почему? isFinite приводит нечисловые значения к числам, возвращая true для всего, что приводится к конечному числу. Если такое поведение желательно, сделайте его явным.

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

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

Тестирование (Testing)

function foo() {
    return true;
}
  • 30.2 Нет, серьезно:
  • Какой бы фреймворк для тестирования вы ни использовали, вы должны писать тесты!
  • Старайтесь писать много маленьких чистых функций и минимизируйте места, где происходят изменения.
  • Будьте осторожны с заглушками и моками — они могут сделать ваши тесты более хрупкими.
  • Мы в основном используем mocha в Airbnb. tape также иногда используется для небольших, отдельных модулей.
  • 100% покрытие тестами — хорошая цель, к которой нужно стремиться, даже если ее не всегда практично достичь.
  • Каждый раз, когда вы исправляете ошибку, пишите регрессионный тест. Ошибка, исправленная без регрессионного теста, почти наверняка сломается снова в будущем.

Производительность (Performance)

Ресурсы (Resources)

Изучайте ES6

Прочтите это

Инструменты

  • Code Style Linters

Другие руководства по стилю

Другие стили

Дополнительная литература

Книги

Блоги

Подкасты

В дикой природе (In the Wild)

Это список организаций, которые используют это руководство по стилю. Отправьте нам пул-реквест или откройте issue, и мы добавим вас в список.

Перевод (Translation)

Это руководство по стилю также доступно на других языках:

Руководство по Руководству по стилю JavaScript (The JavaScript Style Guide Guide)

Общайтесь с нами о JavaScript

  • Найдите нас на gitter.

Участники (Contributors)

Лицензия (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)

Мы рекомендуем вам создать форк этого руководства и изменить правила в соответствии с стилем вашей команды. Ниже вы можете перечислить некоторые поправки к руководству по стилю. Это позволяет вам периодически обновлять руководство по стилю без необходимости разрешать конфликты слияния (merge conflicts).

Reference

All rights reserved,未經允許不得隨意轉載
Создано при помощи Hugo
Тема Stack, дизайн Jimmy