https://github.com/leonidlebedev/javascript-airbnb
Перевод «JavaScript Style Guide» от Airbnb
https://github.com/leonidlebedev/javascript-airbnb
arrow-functions es2015 es2016 es2017 es2018 es2019 es2020 es6 eslint javascript linting naming-conventions react style-guide style-linter styleguide translation
Last synced: 12 months ago
JSON representation
Перевод «JavaScript Style Guide» от Airbnb
- Host: GitHub
- URL: https://github.com/leonidlebedev/javascript-airbnb
- Owner: leonidlebedev
- Created: 2016-12-16T01:01:16.000Z (over 9 years ago)
- Default Branch: master
- Last Pushed: 2021-11-28T09:30:14.000Z (over 4 years ago)
- Last Synced: 2025-04-12T14:16:54.165Z (about 1 year ago)
- Topics: arrow-functions, es2015, es2016, es2017, es2018, es2019, es2020, es6, eslint, javascript, linting, naming-conventions, react, style-guide, style-linter, styleguide, translation
- Homepage: https://leonidlebedev.github.io/javascript-airbnb/
- Size: 510 KB
- Stars: 924
- Watchers: 44
- Forks: 271
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Руководство по написанию JavaScript-кода от [Airbnb](https://github.com/airbnb/javascript/)() {
*Наиболее разумный подход к написанию JavaScript-кода*
> **Замечание**: это руководство подразумевает использование [Babel](https://babeljs.io) вместе с [babel-preset-airbnb](https://npmjs.com/babel-preset-airbnb) или аналогом. Оно также предполагает установленный shims/polyfills в вашем приложении, такой как [airbnb-browser-shims](https://npmjs.com/airbnb-browser-shims) или аналог.
[](https://www.npmjs.com/package/eslint-config-airbnb)
[](https://www.npmjs.com/package/eslint-config-airbnb-base)
[](https://gitter.im/airbnb/javascript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
Это руководство также доступно на других языках. Смотрите [Переводы](#translation).
Другие руководства
- [React](react/)
- [CSS-in-JavaScript](css-in-javascript/)
## Оглавление
1. [Типы](#types)
1. [Объявление переменных](#references)
1. [Объекты](#objects)
1. [Массивы](#arrays)
1. [Деструктуризация](#destructuring)
1. [Строки](#strings)
1. [Функции](#functions)
1. [Стрелочные функции](#arrow-functions)
1. [Классы и конструкторы](#classes--constructors)
1. [Модули](#modules)
1. [Итераторы и генераторы](#iterators-and-generators)
1. [Свойства](#properties)
1. [Переменные](#variables)
1. [Подъём](#hoisting)
1. [Операторы сравнения и равенства](#comparison-operators--equality)
1. [Блоки](#blocks)
1. [Управляющие операторы](#control-statements)
1. [Комментарии](#comments)
1. [Пробелы](#whitespace)
1. [Запятые](#commas)
1. [Точка с запятой](#semicolons)
1. [Приведение типов](#type-casting--coercion)
1. [Соглашение об именовании](#naming-conventions)
1. [Аксессоры](#accessors)
1. [События](#events)
1. [jQuery](#jquery)
1. [Поддержка ECMAScript 5](#ecmascript-5-compatibility)
1. [Возможности ECMAScript 6+ (ES 2015+)](#ecmascript-6-es-2015-styles)
1. [Стандартная библиотека](#standard-library)
1. [Тестирование](#testing)
1. [Производительность](#performance)
1. [Ресурсы](#resources)
1. [В реальной жизни](#in-the-wild)
1. [Переводы](#translation)
1. [Пообщаться с разработчиками Airbnb](#chat-with-us-about-javascript)
1. [Участники перевода](#contributors)
1. [Лицензия](#license)
1. [Поправки](#amendments)
## Типы
- [1.1](#types--primitives) **Простые типы**: Когда вы взаимодействуете с простым типом, вы напрямую работаете с его значением.
- `string`
- `number`
- `boolean`
- `null`
- `undefined`
- `symbol`
- `bigint`
```javascript
const foo = 1;
let bar = foo;
bar = 9;
console.log(foo, bar); // => 1, 9
```
- Symbol и BigInt не могут быть полностью заполифиллены, поэтому они не должны использоваться, если разработка ведётся для браузеров или других сред, которые не поддерживают их нативно.
- [1.2](#types--complex) **Сложные типы**: Когда вы взаимодействуете со сложным типом, вы работаете со ссылкой на его значение.
- `object`
- `array`
- `function`
```javascript
const foo = [1, 2];
const bar = foo;
bar[0] = 9;
console.log(foo[0], bar[0]); // => 9, 9
```
**[⬆ к оглавлению](#Оглавление)**
- [2.1](#references--prefer-const) Используйте `const` для объявления переменных; избегайте `var`. eslint: [`prefer-const`](https://eslint.org/docs/rules/prefer-const.html), [`no-const-assign`](https://eslint.org/docs/rules/no-const-assign.html)
> Почему? Это гарантирует, что вы не сможете переопределять значения, т.к. это может привести к ошибкам и к усложнению понимания кода.
```javascript
// плохо
var a = 1;
var b = 2;
// хорошо
const a = 1;
const b = 2;
```
- [2.2](#references--disallow-var) Если вам необходимо переопределять значения, то используйте `let` вместо `var`. eslint: [`no-var`](https://eslint.org/docs/rules/no-var.html)
> Почему? Область видимости `let` — блок, у `var` — функция.
```javascript
// плохо
var count = 1;
if (true) {
count += 1;
}
// хорошо, используйте let.
let count = 1;
if (true) {
count += 1;
}
```
- [2.3](#references--block-scope) Помните, что у `let` и `const` блочная область видимости, в то время как `var` имеет функциональную область видимости.
```javascript
// const и let существуют только в том блоке, в котором они определены.
{
let a = 1;
const b = 1;
var c = 1;
}
console.log(a); // ReferenceError
console.log(b); // ReferenceError
console.log(c); // 1
```
В приведённом выше коде вы можете видеть, что ссылки на `a` и `b` приведут к ошибке `ReferenceError`, в то время как `c` содержит число. Это связано с тем, что `a` и `b` имеют блочную область видимости, в то время как у `c` функциональная.
**[⬆ к оглавлению](#Оглавление)**
## Объекты
- [3.1](#objects--no-new) Для создания объекта используйте литеральную нотацию. eslint: [`no-new-object`](https://eslint.org/docs/rules/no-new-object.html)
```javascript
// плохо
const item = new Object();
// хорошо
const item = {};
```
- [3.2](#es6-computed-properties) Используйте вычисляемые имена свойств, когда создаёте объекты с динамическими именами свойств.
> Почему? Они позволяют вам определить все свойства объекта в одном месте.
```javascript
function getKey(k) {
return `a key named ${k}`;
}
// плохо
const obj = {
id: 5,
name: 'San Francisco',
};
obj[getKey('enabled')] = true;
// хорошо
const obj = {
id: 5,
name: 'San Francisco',
[getKey('enabled')]: true,
};
```
- [3.3](#es6-object-shorthand) Используйте сокращённую запись метода объекта. eslint: [`object-shorthand`](https://eslint.org/docs/rules/object-shorthand.html)
```javascript
// плохо
const atom = {
value: 1,
addValue: function (value) {
return atom.value + value;
},
};
// хорошо
const atom = {
value: 1,
addValue(value) {
return atom.value + value;
},
};
```
- [3.4](#es6-object-concise) Используйте сокращённую запись свойств объекта. eslint: [`object-shorthand`](https://eslint.org/docs/rules/object-shorthand.html)
> Почему? Это короче и понятнее.
```javascript
const lukeSkywalker = 'Luke Skywalker';
// плохо
const obj = {
lukeSkywalker: lukeSkywalker,
};
// хорошо
const obj = {
lukeSkywalker,
};
```
- [3.5](#objects--grouped-shorthand) Группируйте ваши сокращённые записи свойств в начале объявления объекта.
> Почему? Так легче сказать, какие свойства используют сокращённую запись.
```javascript
const anakinSkywalker = 'Anakin Skywalker';
const lukeSkywalker = 'Luke Skywalker';
// плохо
const obj = {
episodeOne: 1,
twoJediWalkIntoACantina: 2,
lukeSkywalker,
episodeThree: 3,
mayTheFourth: 4,
anakinSkywalker,
};
// хорошо
const obj = {
lukeSkywalker,
anakinSkywalker,
episodeOne: 1,
twoJediWalkIntoACantina: 2,
episodeThree: 3,
mayTheFourth: 4,
};
```
- [3.6](#objects--quoted-props) Только недопустимые идентификаторы помещаются в кавычки. eslint: [`quote-props`](https://eslint.org/docs/rules/quote-props.html)
> Почему? На наш взгляд, такой код легче читать. Это улучшает подсветку синтаксиса, а также облегчает оптимизацию для многих JS-движков.
```javascript
// плохо
const bad = {
'foo': 3,
'bar': 4,
'data-blah': 5,
};
// хорошо
const good = {
foo: 3,
bar: 4,
'data-blah': 5,
};
```
- [3.7](#objects--prototype-builtins) Не вызывайте напрямую методы `Object.prototype`, такие как `hasOwnProperty`, `propertyIsEnumerable`, и `isPrototypeOf`. eslint: [`no-prototype-builtins`](https://eslint.org/docs/rules/no-prototype-builtins)
> Почему? Эти методы могут быть переопределены в свойствах объекта, который мы проверяем `{ hasOwnProperty: false }`, или этот объект может быть `null` (`Object.create(null)`).
```javascript
// плохо
console.log(object.hasOwnProperty(key));
// хорошо
console.log(Object.prototype.hasOwnProperty.call(object, key));
// отлично
const has = Object.prototype.hasOwnProperty; // Кэшируем запрос в рамках модуля.
console.log(has.call(object, key));
/* или */
import has from 'has'; // https://www.npmjs.com/package/has
console.log(has(object, key));
```
- [3.8](#objects--rest-spread) Используйте синтаксис расширения вместо [`Object.assign`](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) для поверхностного копирования объектов. Используйте параметр оставшихся свойств, чтобы получить новый объект с некоторыми опущенными свойствами. eslint: [`prefer-object-spread`](https://eslint.org/docs/rules/prefer-object-spread)
```javascript
// очень плохо
const original = { a: 1, b: 2 };
const copy = Object.assign(original, { c: 3 }); // эта переменная изменяет `original` ಠ_ಠ
delete copy.a; // если сделать так
// плохо
const original = { a: 1, b: 2 };
const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }
// хорошо
const original = { a: 1, b: 2 };
const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }
const { a, ...noA } = copy; // noA => { b: 2, c: 3 }
```
**[⬆ к оглавлению](#Оглавление)**
## Массивы
- [4.1](#arrays--literals) Для создания массива используйте литеральную нотацию. eslint: [`no-array-constructor`](https://eslint.org/docs/rules/no-array-constructor.html)
```javascript
// плохо
const items = new Array();
// хорошо
const items = [];
```
- [4.2](#arrays--push) Для добавления элемента в массив используйте [Array#push](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Array/push) вместо прямого присваивания.
```javascript
const someStack = [];
// плохо
someStack[someStack.length] = 'abracadabra';
// хорошо
someStack.push('abracadabra');
```
- [4.3](#es6-array-spreads) Для копирования массивов используйте оператор расширения `...`.
```javascript
// плохо
const len = items.length;
const itemsCopy = [];
let i;
for (i = 0; i < len; i += 1) {
itemsCopy[i] = items[i];
}
// хорошо
const itemsCopy = [...items];
```
- [4.4](#arrays--from-iterable) Для преобразования итерируемого объекта в массив используйте оператор расширения `...` вместо [`Array.from`](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Array/from).
```javascript
const foo = document.querySelectorAll('.foo');
// хорошо
const nodes = Array.from(foo);
// отлично
const nodes = [...foo];
```
- [4.5](#arrays--from-array-like) Используйте [`Array.from`](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Array/from) для преобразования массивоподобного объекта в массив.
```javascript
const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 };
// плохо
const arr = Array.prototype.slice.call(arrLike);
// хорошо
const arr = Array.from(arrLike);
```
- [4.6](#arrays--mapping) Используйте [`Array.from`](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Array/from) вместо оператора расширения `...` для маппинга итерируемых объектов, это позволяет избежать создания промежуточного массива.
```javascript
// плохо
const baz = [...foo].map(bar);
// хорошо
const baz = Array.from(foo, bar);
```
- [4.7](#arrays--callback-return) Используйте операторы `return` внутри функций обратного вызова в методах массива. Можно опустить `return`, когда тело функции состоит из одной инструкции, возвращающей выражение без побочных эффектов. [8.2](#arrows--implicit-return). eslint: [`array-callback-return`](https://eslint.org/docs/rules/array-callback-return)
```javascript
// хорошо
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});
// хорошо
[1, 2, 3].map((x) => x + 1);
// плохо - нет возвращаемого значения, следовательно, `acc` становится `undefined` после первой итерации
[[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
const flatten = acc.concat(item);
});
// хорошо
[[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => {
const flatten = acc.concat(item);
return flatten;
});
// плохо
inbox.filter((msg) => {
const { subject, author } = msg;
if (subject === 'Mockingbird') {
return author === 'Harper Lee';
} else {
return false;
}
});
// хорошо
inbox.filter((msg) => {
const { subject, author } = msg;
if (subject === 'Mockingbird') {
return author === 'Harper Lee';
}
return false;
});
```
- [4.8](#arrays--bracket-newline) Если массив располагается на нескольких строках, то используйте разрывы строк после открытия и перед закрытием скобок.
```javascript
// плохо
const arr = [
[0, 1], [2, 3], [4, 5],
];
const objectInArray = [{
id: 1,
}, {
id: 2,
}];
const numberInArray = [
1, 2,
];
// хорошо
const arr = [[0, 1], [2, 3], [4, 5]];
const objectInArray = [
{
id: 1,
},
{
id: 2,
},
];
const numberInArray = [
1,
2,
];
```
**[⬆ к оглавлению](#Оглавление)**
- [5.1](#destructuring--object) При обращении к нескольким свойствам объекта используйте деструктуризацию объекта. eslint: [`prefer-destructuring`](https://eslint.org/docs/rules/prefer-destructuring)
> Почему? Деструктуризация сохраняет вас от создания временных переменных для этих свойств и от повторного доступа к объекту. Повторный доступ к объектам создаёт более повторяющийся код, требует большего чтения и создаёт больше возможностей для ошибок. Деструктуризация объектов также обеспечивает единое местоположение определения структуры объекта, которое используется в блоке, вместо того, чтобы требовать чтения всего блока для определения того, что используется.
```javascript
// плохо
function getFullName(user) {
const firstName = user.firstName;
const lastName = user.lastName;
return `${firstName} ${lastName}`;
}
// хорошо
function getFullName(user) {
const { firstName, lastName } = user;
return `${firstName} ${lastName}`;
}
// отлично
function getFullName({ firstName, lastName }) {
return `${firstName} ${lastName}`;
}
```
- [5.2](#destructuring--array) Используйте деструктуризацию массивов. eslint: [`prefer-destructuring`](https://eslint.org/docs/rules/prefer-destructuring)
```javascript
const arr = [1, 2, 3, 4];
// плохо
const first = arr[0];
const second = arr[1];
// хорошо
const [first, second] = arr;
```
- [5.3](#destructuring--object-over-array) Используйте деструктуризацию объекта для множества возвращаемых значений, но не делайте тоже самое с массивами.
> Почему? Вы сможете добавить новые свойства через некоторое время или изменить порядок без последствий.
```javascript
// плохо
function processInput(input) {
// затем происходит чудо
return [left, right, top, bottom];
}
// при вызове нужно подумать о порядке возвращаемых данных
const [left, __, top] = processInput(input);
// хорошо
function processInput(input) {
// затем происходит чудо
return { left, right, top, bottom };
}
// при вызове выбираем только необходимые данные
const { left, top } = processInput(input);
```
**[⬆ к оглавлению](#Оглавление)**
## Строки
- [6.1](#strings--quotes) Используйте одинарные кавычки `''` для строк. eslint: [`quotes`](https://eslint.org/docs/rules/quotes.html)
```javascript
// плохо
const name = "Capt. Janeway";
// плохо - литерал шаблонной строки должен содержать интерполяцию или переводы строк
const name = `Capt. Janeway`;
// хорошо
const name = 'Capt. Janeway';
```
- [6.2](#strings--line-length) Строки, у которых в строчке содержится более 100 символов, не пишутся на нескольких строчках с использованием конкатенации.
> Почему? Работать с разбитыми строками неудобно и это затрудняет поиск по коду.
```javascript
// плохо
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.';
// плохо
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.';
// хорошо
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.';
```
- [6.3](#es6-template-literals) При создании строки программным путём используйте шаблонные строки вместо конкатенации. eslint: [`prefer-template`](https://eslint.org/docs/rules/prefer-template.html) [`template-curly-spacing`](https://eslint.org/docs/rules/template-curly-spacing)
> Почему? Шаблонные строки дают вам читабельность, лаконичный синтаксис с правильными символами перевода строк и функции интерполяции строки.
```javascript
// плохо
function sayHi(name) {
return 'How are you, ' + name + '?';
}
// плохо
function sayHi(name) {
return ['How are you, ', name, '?'].join();
}
// плохо
function sayHi(name) {
return `How are you, ${ name }?`;
}
// хорошо
function sayHi(name) {
return `How are you, ${name}?`;
}
```
- [6.4](#strings--eval) Никогда не используйте `eval()`, т.к. это открывает множество уязвимостей. eslint: [`no-eval`](https://eslint.org/docs/rules/no-eval)
- [6.5](#strings--escaping) Не используйте в строках необязательные экранирующие символы. eslint: [`no-useless-escape`](https://eslint.org/docs/rules/no-useless-escape)
> Почему? Обратные слеши ухудшают читабельность, поэтому они должны быть только при необходимости.
```javascript
// плохо
const foo = '\'this\' \i\s \"quoted\"';
// хорошо
const foo = '\'this\' is "quoted"';
const foo = `my name is '${name}'`;
```
**[⬆ к оглавлению](#Оглавление)**
## Функции
- [7.1](#functions--declarations) Используйте функциональные выражения вместо объявлений функций. eslint: [`func-style`](https://eslint.org/docs/rules/func-style)
> Почему? У объявлений функций есть подъём. Это означает, что можно использовать функцию до того, как она определена в файле, но это вредит читабельности и поддержке. Если вы обнаружили, что определение функции настолько большое или сложное, что мешает понимать остальную часть файла, то, возможно, пришло время извлечь его в отдельный модуль. Не забудьте явно назвать функциональное выражение, независимо от того, подразумевается ли имя из содержащейся переменной (такое часто бывает в современных браузерах или при использовании компиляторов, таких как Babel). Это помогает точнее определять место ошибки по стеку вызовов. ([Обсуждение](https://github.com/airbnb/javascript/issues/794))
```javascript
// плохо
function foo() {
// ...
}
// плохо
const foo = function () {
// ...
};
// хорошо
// лексическое имя, отличное от вызываемой(-ых) переменной(-ых)
const foo = function uniqueMoreDescriptiveLexicalFoo() {
// ...
};
```
- [7.2](#functions--iife) Оборачивайте в скобки немедленно вызываемые функции. eslint: [`wrap-iife`](https://eslint.org/docs/rules/wrap-iife.html)
> Почему? Немедленно вызываемая функция представляет собой единый блок. Чтобы чётко показать это — оберните функцию и вызывающие скобки в ещё одни скобки. Обратите внимание, что в мире с модулями вам больше не нужны немедленно вызываемые функции.
```javascript
// Немедленно вызываемая функция
(function () {
console.log('Welcome to the Internet. Please follow me.');
}());
```
- [7.3](#functions--in-blocks) Никогда не объявляйте функции в нефункциональном блоке (`if`, `while`, и т.д.). Вместо этого присвойте функцию переменной. Браузеры позволяют выполнить ваш код, но все они интерпретируют его по-разному. eslint: [`no-loop-func`](https://eslint.org/docs/rules/no-loop-func.html)
- [7.4](#functions--note-on-blocks) **Примечание:** ECMA-262 определяет `блок` как список инструкций. Объявление функции не является инструкцией.
```javascript
// плохо
if (currentUser) {
function test() {
console.log('Nope.');
}
}
// хорошо
let test;
if (currentUser) {
test = () => {
console.log('Yup.');
};
}
```
- [7.5](#functions--arguments-shadow) Никогда не называйте параметр `arguments`. Он будет иметь приоритет над объектом `arguments`, который доступен для каждой функции.
```javascript
// плохо
function foo(name, options, arguments) {
// ...
}
// хорошо
function foo(name, options, args) {
// ...
}
```
- [7.6](#es6-rest) Никогда не используйте `arguments`, вместо этого используйте синтаксис оставшихся параметров `...`. eslint: [`prefer-rest-params`](https://eslint.org/docs/rules/prefer-rest-params)
> Почему? `...` явно говорит о том, какие именно аргументы вы хотите извлечь. Кроме того, такой синтаксис создаёт настоящий массив, а не массивоподобный объект как `arguments`.
```javascript
// плохо
function concatenateAll() {
const args = Array.prototype.slice.call(arguments);
return args.join('');
}
// хорошо
function concatenateAll(...args) {
return args.join('');
}
```
- [7.7](#es6-default-parameters) Используйте синтаксис записи аргументов по умолчанию, а не изменяйте аргументы функции.
```javascript
// очень плохо
function handleThings(opts) {
// Нет! Мы не должны изменять аргументы функции.
// Плохо вдвойне: если переменная opts будет ложной,
// то ей присвоится пустой объект, а не то что вы хотели.
// Это приведёт к коварным ошибкам.
opts = opts || {};
// ...
}
// всё ещё плохо
function handleThings(opts) {
if (opts === void 0) {
opts = {};
}
// ...
}
// хорошо
function handleThings(opts = {}) {
// ...
}
```
- [7.8](#functions--default-side-effects) Избегайте побочных эффектов с параметрами по умолчанию.
> Почему? И так всё понятно.
```javascript
var b = 1;
// плохо
function count(a = b++) {
console.log(a);
}
count(); // 1
count(); // 2
count(3); // 3
count(); // 3
```
- [7.9](#functions--defaults-last) Всегда вставляйте последними параметры по умолчанию. eslint: [`default-param-last`](https://eslint.org/docs/rules/default-param-last)
```javascript
// плохо
function handleThings(opts = {}, name) {
// ...
}
// хорошо
function handleThings(name, opts = {}) {
// ...
}
```
- [7.10](#functions--constructor) Никогда не используйте конструктор функций для создания новых функий. eslint: [`no-new-func`](https://eslint.org/docs/rules/no-new-func)
> Почему? Создание функции в таком духе вычисляет строку подобно `eval()`, из-за чего открываются уязвимости.
```javascript
// плохо
var add = new Function('a', 'b', 'return a + b');
// всё ещё плохо
var subtract = Function('a', 'b', 'return a - b');
```
- [7.11](#functions--signature-spacing) Отступы при определении функции. eslint: [`space-before-function-paren`](https://eslint.org/docs/rules/space-before-function-paren) [`space-before-blocks`](https://eslint.org/docs/rules/space-before-blocks)
> Почему? Однородность кода — это хорошо. Вам не надо будет добавлять или удалять пробел при манипуляции с именем.
```javascript
// плохо
const f = function(){};
const g = function (){};
const h = function() {};
// хорошо
const x = function () {};
const y = function a() {};
```
- [7.12](#functions--mutate-params) Никогда не изменяйте параметры. eslint: [`no-param-reassign`](https://eslint.org/docs/rules/no-param-reassign.html)
> Почему? Манипуляция объектами, приходящими в качестве параметров, может вызывать нежелательные побочные эффекты в вызывающей функции.
```javascript
// плохо
function f1(obj) {
obj.key = 1;
}
// хорошо
function f2(obj) {
const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
}
```
- [7.13](#functions--reassign-params) Никогда не переназначайте параметры. eslint: [`no-param-reassign`](https://eslint.org/docs/rules/no-param-reassign.html)
> Почему? Переназначенные параметры могут привести к неожиданному поведению, особенно при обращении к `arguments`. Это также может вызывать проблемы оптимизации, особенно в V8.
```javascript
// плохо
function f1(a) {
a = 1;
// ...
}
function f2(a) {
if (!a) { a = 1; }
// ...
}
// хорошо
function f3(a) {
const b = a || 1;
// ...
}
function f4(a = 1) {
// ...
}
```
- [7.14](#functions--spread-vs-apply) Отдавайте предпочтение использованию синтаксис расширения `...` при вызове вариативной функции. eslint: [`prefer-spread`](https://eslint.org/docs/rules/prefer-spread)
> Почему? Это чище, вам не нужно предоставлять контекст, и не так просто составить `new` с `apply`.
```javascript
// плохо
const x = [1, 2, 3, 4, 5];
console.log.apply(console, x);
// хорошо
const x = [1, 2, 3, 4, 5];
console.log(...x);
// плохо
new (Function.prototype.bind.apply(Date, [null, 2016, 8, 5]));
// хорошо
new Date(...[2016, 8, 5]);
```
- [7.15](#functions--signature-invocation-indentation) Функции с многострочным определением или запуском должны содержать такие же отступы, как и другие многострочные списки в этом руководстве: с каждым элементом на отдельной строке, с запятой в конце элемента. eslint: [`function-paren-newline`](https://eslint.org/docs/rules/function-paren-newline)
```javascript
// плохо
function foo(bar,
baz,
quux) {
// ...
}
// хорошо
function foo(
bar,
baz,
quux,
) {
// ...
}
// плохо
console.log(foo,
bar,
baz);
// хорошо
console.log(
foo,
bar,
baz,
);
```
**[⬆ к оглавлению](#Оглавление)**
- [8.1](#arrows--use-them) Когда вам необходимо использовать анонимную функцию (например, при передаче встроенной функции обратного вызова), используйте стрелочную функцию. eslint: [`prefer-arrow-callback`](https://eslint.org/docs/rules/prefer-arrow-callback.html), [`arrow-spacing`](https://eslint.org/docs/rules/arrow-spacing.html)
> Почему? Таким образом создаётся функция, которая выполняется в контексте `this`, который мы обычно хотим, а также это более короткий синтаксис.
> Почему бы и нет? Если у вас есть довольно сложная функция, вы можете переместить эту логику внутрь её собственного именованного функционального выражения.
```javascript
// плохо
[1, 2, 3].map(function (x) {
const y = x + 1;
return x * y;
});
// хорошо
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});
```
- [8.2](#arrows--implicit-return) Если тело функции состоит из одного оператора, возвращающего [выражение](https://developer.mozilla.org/ru/docs/Web/JavaScript/Guide/Expressions_and_Operators#Выражения) без побочных эффектов, то опустите фигурные скобки и используйте неявное возвращение. В противном случае, сохраните фигурные скобки и используйте оператор `return`. eslint: [`arrow-parens`](https://eslint.org/docs/rules/arrow-parens.html), [`arrow-body-style`](https://eslint.org/docs/rules/arrow-body-style.html)
> Почему? Синтаксический сахар. Когда несколько функций соединены вместе, то это лучше читается.
```javascript
// плохо
[1, 2, 3].map((number) => {
const nextNumber = number + 1;
`A string containing the ${nextNumber}.`;
});
// хорошо
[1, 2, 3].map((number) => `A string containing the ${number + 1}.`);
// хорошо
[1, 2, 3].map((number) => {
const nextNumber = number + 1;
return `A string containing the ${nextNumber}.`;
});
// хорошо
[1, 2, 3].map((number, index) => ({
[index]: number,
}));
// Неявный возврат с побочными эффектами
function foo(callback) {
const val = callback();
if (val === true) {
// Сделать что-то, если функция обратного вызова вернёт true
}
}
let bool = false;
// плохо
foo(() => bool = true);
// хорошо
foo(() => {
bool = true;
});
```
- [8.3](#arrows--paren-wrap) В случае, если выражение располагается на нескольких строках, то необходимо обернуть его в скобки для лучшей читаемости.
> Почему? Это чётко показывает, где функция начинается и где заканчивается.
```javascript
// плохо
['get', 'post', 'put'].map((httpMethod) => Object.prototype.hasOwnProperty.call(
httpMagicObjectWithAVeryLongName,
httpMethod,
)
);
// хорошо
['get', 'post', 'put'].map((httpMethod) => (
Object.prototype.hasOwnProperty.call(
httpMagicObjectWithAVeryLongName,
httpMethod,
)
));
```
- [8.4](#arrows--one-arg-parens) Всегда оборачивайте аргументы круглыми скобками для ясности и согласованности. eslint: [`arrow-parens`](https://eslint.org/docs/rules/arrow-parens.html)
> Почему? Минимизирует различия при удалении или добавлении аргументов.
```javascript
// плохо
[1, 2, 3].map(x => x * x);
// хорошо
[1, 2, 3].map((x) => x * x);
// плохо
[1, 2, 3].map(number => (
`A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!`
));
// хорошо
[1, 2, 3].map((number) => (
`A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!`
));
// плохо
[1, 2, 3].map(x => {
const y = x + 1;
return x * y;
});
// хорошо
[1, 2, 3].map((x) => {
const y = x + 1;
return x * y;
});
```
- [8.5](#arrows--confusing) Избегайте схожести стрелочной функции (`=>`) с операторами сравнения (`<=`, `>=`). eslint: [`no-confusing-arrow`](https://eslint.org/docs/rules/no-confusing-arrow)
```javascript
// плохо
const itemHeight = (item) => item.height <= 256 ? item.largeSize : item.smallSize;
// плохо
const itemHeight = (item) => item.height >= 256 ? item.largeSize : item.smallSize;
// хорошо
const itemHeight = (item) => (item.height <= 256 ? item.largeSize : item.smallSize);
// хорошо
const itemHeight = (item) => {
const { height, largeSize, smallSize } = item;
return height <= 256 ? largeSize : smallSize;
};
```
- [8.6](#whitespace--implicit-arrow-linebreak) Зафиксируйте расположение тела стрелочной функции с неявным возвратом. eslint: [`implicit-arrow-linebreak`](https://eslint.org/docs/rules/implicit-arrow-linebreak)
```javascript
// плохо
(foo) =>
bar;
(foo) =>
(bar);
// хорошо
(foo) => bar;
(foo) => (bar);
(foo) => (
bar
)
```
**[⬆ к оглавлению](#Оглавление)**
- [9.1](#constructors--use-class) Всегда используйте `class`. Избегайте прямых манипуляций с `prototype`.
> Почему? Синтаксис `class` является кратким и понятным.
```javascript
// плохо
function Queue(contents = []) {
this.queue = [...contents];
}
Queue.prototype.pop = function () {
const value = this.queue[0];
this.queue.splice(0, 1);
return value;
};
// хорошо
class Queue {
constructor(contents = []) {
this.queue = [...contents];
}
pop() {
const value = this.queue[0];
this.queue.splice(0, 1);
return value;
}
}
```
- [9.2](#constructors--extends) Используйте `extends` для наследования.
> Почему? Это встроенный способ наследовать функциональность прототипа, не нарушая `instanceof`.
```javascript
// плохо
const inherits = require('inherits');
function PeekableQueue(contents) {
Queue.apply(this, contents);
}
inherits(PeekableQueue, Queue);
PeekableQueue.prototype.peek = function () {
return this.queue[0];
};
// хорошо
class PeekableQueue extends Queue {
peek() {
return this.queue[0];
}
}
```
- [9.3](#constructors--chaining) Методы могут возвращать `this`, чтобы делать цепочки вызовов.
```javascript
// плохо
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
// хорошо
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](#constructors--tostring) Вы можете определить свой собственный метод `toString()`, просто убедитесь, что он успешно работает и не создаёт никаких побочных эффектов.
```javascript
class Jedi {
constructor(options = {}) {
this.name = options.name || 'no name';
}
getName() {
return this.name;
}
toString() {
return `Jedi - ${this.getName()}`;
}
}
```
- [9.5](#constructors--no-useless) У классов есть конструктор по умолчанию, если он не задан явно. Можно опустить пустой конструктор или конструктор, который только делегирует выполнение родительскому классу. eslint: [`no-useless-constructor`](https://eslint.org/docs/rules/no-useless-constructor)
```javascript
// плохо
class Jedi {
constructor() {}
getName() {
return this.name;
}
}
// плохо
class Rey extends Jedi {
constructor(...args) {
super(...args);
}
}
// хорошо
class Rey extends Jedi {
constructor(...args) {
super(...args);
this.name = 'Rey';
}
}
```
- [9.6](#classes--no-duplicate-members) Избегайте дублирующих членов класса. eslint: [`no-dupe-class-members`](https://eslint.org/docs/rules/no-dupe-class-members)
> Почему? Если объявление члена класса повторяется, без предупреждения будет использовано последнее. Наличие дубликатов скорее всего приведёт к ошибке.
```javascript
// плохо
class Foo {
bar() { return 1; }
bar() { return 2; }
}
// хорошо
class Foo {
bar() { return 1; }
}
// хорошо
class Foo {
bar() { return 2; }
}
```
- [9.7](#classes--methods-use-this) Метод класса должен использовать `this` или быть преобразованным в статический метод, если только внешняя библиотека или фреймворк не требуют использования определённых нестатических методов. Будучи методом экземпляра, следует указать, что он ведёт себя по-разному в зависимости от свойств получателя. eslint: [`class-methods-use-this`](https://eslint.org/docs/rules/class-methods-use-this)
```javascript
// плохо
class Foo {
bar() {
console.log('bar');
}
}
// хорошо - используется this
class Foo {
bar() {
console.log(this.bar);
}
}
// хорошо - конструктор освобождается
class Foo {
constructor() {
// ...
}
}
// хорошо - статические методы не должны использовать this
class Foo {
static bar() {
console.log('bar');
}
}
```
**[⬆ к оглавлению](#Оглавление)**
## Модули
- [10.1](#modules--use-them) Всегда используйте модули (`import`/`export`) вместо нестандартных модульных систем. Вы всегда сможете транспилировать код в вашу любимую модульную систему.
> Почему? Модули — это будущее. Давайте начнём использовать будущее уже сейчас!
```javascript
// плохо
const AirbnbStyleGuide = require('./AirbnbStyleGuide');
module.exports = AirbnbStyleGuide.es6;
// ok
import AirbnbStyleGuide from './AirbnbStyleGuide';
export default AirbnbStyleGuide.es6;
// отлично
import { es6 } from './AirbnbStyleGuide';
export default es6;
```
- [10.2](#modules--no-wildcard) Не используйте импорт через `*`.
> Почему? Это гарантирует, что у вас есть единственный экспорт по умолчанию.
```javascript
// плохо
import * as AirbnbStyleGuide from './AirbnbStyleGuide';
// хорошо
import AirbnbStyleGuide from './AirbnbStyleGuide';
```
- [10.3](#modules--no-export-from-import) Не экспортируйте прямо из импорта.
> Почему? Несмотря на то, что запись в одну строку является краткой, разделение на отдельные строки делает вещи последовательными.
```javascript
// плохо
// файл es6.js
export { es6 as default } from './AirbnbStyleGuide';
// хорошо
// файл es6.js
import { es6 } from './AirbnbStyleGuide';
export default es6;
```
- [10.4](#modules--no-duplicate-imports) Импортируйте из пути только один раз.
eslint: [`no-duplicate-imports`](https://eslint.org/docs/rules/no-duplicate-imports)
> Почему? Наличие нескольких строк, которые импортируют из одного и того же файла, может сделать код неподдерживаемым.
```javascript
// плохо
import foo from 'foo';
// … какие-то другие импорты … //
import { named1, named2 } from 'foo';
// хорошо
import foo, { named1, named2 } from 'foo';
// хорошо
import foo, {
named1,
named2,
} from 'foo';
```
- [10.5](#modules--no-mutable-exports) Не экспортируйте изменяемые переменные.
eslint: [`import/no-mutable-exports`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-mutable-exports.md)
> Почему? Вообще, следует избегать мутации, в особенности при экспорте изменяемых переменных. Несмотря на то, что эта техника может быть необходима в редких случаях, в основном только константа должна быть экспортирована.
```javascript
// плохо
let foo = 3;
export { foo };
// хорошо
const foo = 3;
export { foo };
```
- [10.6](#modules--prefer-default-export) В модулях с единственным экспортом предпочтительнее использовать экспорт по умолчанию, а не экспорт по имени.
eslint: [`import/prefer-default-export`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/prefer-default-export.md)
> Почему? Для того чтобы поощрять создание множества файлов, которые бы экспортировали одну сущность, т.к. это лучше для читабельности и поддержки кода.
```javascript
// плохо
export function foo() {}
// хорошо
export default function foo() {}
```
- [10.7](#modules--imports-first) Поместите все импорты выше остальных инструкций.
eslint: [`import/first`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/first.md)
> Почему? Так как `import` обладает подъёмом, то хранение их всех в начале файла предотвращает от неожиданного поведения.
```javascript
// плохо
import foo from 'foo';
foo.init();
import bar from 'bar';
// хорошо
import foo from 'foo';
import bar from 'bar';
foo.init();
```
- [10.8](#modules--multiline-imports-over-newlines) Импорты на нескольких строках должны быть с отступами как у многострочных литералов массива и объекта. eslint: [`object-curly-newline`](https://eslint.org/docs/rules/object-curly-newline)
> Почему? Фигурные скобки следуют тем же правилам отступа как и любая другая фигурная скобка блока в этом руководстве, тоже самое касается висячих запятых.
```javascript
// плохо
import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path';
// хорошо
import {
longNameA,
longNameB,
longNameC,
longNameD,
longNameE,
} from 'path';
```
- [10.9](#modules--no-webpack-loader-syntax) Запретите синтаксис загрузчика Webpack в импорте.
eslint: [`import/no-webpack-loader-syntax`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-webpack-loader-syntax.md)
> Почему? Использование Webpack синтаксиса связывает код с упаковщиком модулей. Предпочтительно использовать синтаксис загрузчика в `webpack.config.js`.
```javascript
// плохо
import fooSass from 'css!sass!foo.scss';
import barCss from 'style!css!bar.css';
// хорошо
import fooSass from 'foo.scss';
import barCss from 'bar.css';
```
- [10.10](#modules--import-extensions) Не указывайте JavaScript расширения файлов
eslint: [`import/extensions`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/extensions.md)
> Почему? Добавление расширений препятствует рефакторингу и нецелесообразно жёстко программируются детали реализации модуля, который вы импортируете в каждом потребителе.
```javascript
// плохо
import foo from './foo.js';
import bar from './bar.jsx';
import baz from './baz/index.jsx';
// хорошо
import foo from './foo';
import bar from './bar';
import baz from './baz';
```
**[⬆ к оглавлению](#Оглавление)**
- [11.1](#iterators--nope) Не используйте итераторы. Применяйте функции высшего порядка вместо таких циклов как `for-in` или `for-of`. eslint: [`no-iterator`](https://eslint.org/docs/rules/no-iterator.html) [`no-restricted-syntax`](https://eslint.org/docs/rules/no-restricted-syntax)
> Почему? Это обеспечивает соблюдение нашего правила о неизменности переменных. Работать с чистыми функциями, которые возвращают значение, проще, чем с функциями с побочными эффектами.
> Используйте `map()` / `every()` / `filter()` / `find()` / `findIndex()` / `reduce()` / `some()` / ... для итерации по массивам, а `Object.keys()` / `Object.values()` / `Object.entries()` для создания массивов, с помощью которых можно итерироваться по объектам.
```javascript
const numbers = [1, 2, 3, 4, 5];
// плохо
let sum = 0;
for (let num of numbers) {
sum += num;
}
sum === 15;
// хорошо
let sum = 0;
numbers.forEach((num) => {
sum += num;
});
sum === 15;
// отлично (используйте силу функций)
const sum = numbers.reduce((total, num) => total + num, 0);
sum === 15;
// плохо
const increasedByOne = [];
for (let i = 0; i < numbers.length; i++) {
increasedByOne.push(numbers[i] + 1);
}
// хорошо
const increasedByOne = [];
numbers.forEach((num) => {
increasedByOne.push(num + 1);
});
// отлично (продолжайте в том же духе)
const increasedByOne = numbers.map((num) => num + 1);
```
- [11.2](#generators--nope) Не используйте пока генераторы.
> Почему? Они не очень хорошо транспилируются в ES5.
- [11.3](#generators--spacing) Если всё-таки необходимо использовать генераторы, или вы не обратили внимания на [наш совет](#generators--nope), убедитесь, что `*` у функции генератора расположена должным образом. eslint: [`generator-star-spacing`](https://eslint.org/docs/rules/generator-star-spacing)
> Почему? `function` и `*` являются частью одного и того же ключевого слова. `*` не является модификатором для `function`, `function*` является уникальной конструкцией, отличной от `function`.
```javascript
// плохо
function * foo() {
// ...
}
const bar = function * () {
// ...
};
const baz = function *() {
// ...
};
const quux = function*() {
// ...
};
function*foo() {
// ...
}
function *foo() {
// ...
}
// очень плохо
function
*
foo() {
// ...
}
const wat = function
*
() {
// ...
};
// хорошо
function* foo() {
// ...
}
const foo = function* () {
// ...
};
```
**[⬆ к оглавлению](#Оглавление)**
## Свойства
- [12.1](#properties--dot) Используйте точечную нотацию для доступа к свойствам. eslint: [`dot-notation`](https://eslint.org/docs/rules/dot-notation.html)
```javascript
const luke = {
jedi: true,
age: 28,
};
// плохо
const isJedi = luke['jedi'];
// хорошо
const isJedi = luke.jedi;
```
- [12.2](#properties--bracket) Используйте скобочную нотацию `[]`, когда название свойства хранится в переменной.
```javascript
const luke = {
jedi: true,
age: 28,
};
function getProp(prop) {
return luke[prop];
}
const isJedi = getProp('jedi');
```
- [12.3](#es2016-properties--exponentiation-operator) Используйте оператор `**` для возведения в степень. eslint: [`no-restricted-properties`](https://eslint.org/docs/rules/no-restricted-properties).
```javascript
// плохо
const binary = Math.pow(2, 10);
// хорошо
const binary = 2 ** 10;
```
**[⬆ к оглавлению](#Оглавление)**
## Переменные
- [13.1](#variables--const) Всегда используйте `const` или `let` для объявления переменных. Невыполнение этого требования приведёт к появлению глобальных переменных. Необходимо избегать загрязнения глобального пространства имён. eslint: [`no-undef`](https://eslint.org/docs/rules/no-undef) [`prefer-const`](https://eslint.org/docs/rules/prefer-const)
```javascript
// плохо
superPower = new SuperPower();
// хорошо
const superPower = new SuperPower();
```
- [13.2](#variables--one-const) Используйте объявление `const` или `let` для каждой переменной или присвоения. eslint: [`one-var`](https://eslint.org/docs/rules/one-var.html)
> Почему? Таким образом проще добавить новые переменные. Также вы никогда не будете беспокоиться о перемещении `;` и `,` и об отображении изменений в пунктуации. Вы также можете пройтись по каждому объявлению с помощью отладчика, вместо того, чтобы прыгать через все сразу.
```javascript
// плохо
const items = getItems(),
goSportsTeam = true,
dragonball = 'z';
// плохо
// (сравните с кодом выше и попытайтесь найти ошибку)
const items = getItems(),
goSportsTeam = true;
dragonball = 'z';
// хорошо
const items = getItems();
const goSportsTeam = true;
const dragonball = 'z';
```
- [13.3](#variables--const-let-group) В первую очередь группируйте `const`, а затем `let`.
> Почему? Это полезно, когда в будущем вам понадобится создать переменную, зависимую от предыдущих.
```javascript
// плохо
let i, len, dragonball,
items = getItems(),
goSportsTeam = true;
// плохо
let i;
const items = getItems();
let dragonball;
const goSportsTeam = true;
let len;
// хорошо
const goSportsTeam = true;
const items = getItems();
let dragonball;
let i;
let length;
```
- [13.4](#variables--define-where-used) Создавайте переменные там, где они вам необходимы, но помещайте их в подходящее место.
> Почему? `let` и `const` имеют блочную область видимости, а не функциональную.
```javascript
// плохо - вызов ненужной функции
function checkName(hasName) {
const name = getName();
if (hasName === 'test') {
return false;
}
if (name === 'test') {
this.setName('');
return false;
}
return name;
}
// хорошо
function checkName(hasName) {
if (hasName === 'test') {
return false;
}
const name = getName();
if (name === 'test') {
this.setName('');
return false;
}
return name;
}
```
- [13.5](#variables--no-chain-assignment) Не создавайте цепочки присваивания переменных. eslint: [`no-multi-assign`](https://eslint.org/docs/rules/no-multi-assign)
> Почему? Такие цепочки создают неявные глобальные переменные.
```javascript
// плохо
(function example() {
// JavaScript интерпретирует это, как
// let a = ( b = ( c = 1 ) );
// Ключевое слово let применится только к переменной a;
// переменные b и c станут глобальными.
let a = b = c = 1;
}());
console.log(a); // throws ReferenceError
console.log(b); // 1
console.log(c); // 1
// хорошо
(function example() {
let a = 1;
let b = a;
let c = a;
}());
console.log(a); // throws ReferenceError
console.log(b); // throws ReferenceError
console.log(c); // throws ReferenceError
// тоже самое и для `const`
```
- [13.6](#variables--unary-increment-decrement) Избегайте использования унарных инкрементов и декрементов (`++`, `--`). eslint [`no-plusplus`](https://eslint.org/docs/rules/no-plusplus)
> Почему? Согласно документации eslint, унарные инкремент и декремент автоматически вставляют точку с запятой, что может стать причиной трудноуловимых ошибок при инкрементировании и декрементировании значений. Также нагляднее изменять ваши значения таким образом `num += 1` вместо `num++` или `num ++`. Запрет на унарные инкремент и декремент ограждает вас от непреднамеренных преждевременных инкрементаций/декрементаций значений, которые могут привести к непредсказуемому поведению вашей программы.
```javascript
// плохо
const array = [1, 2, 3];
let num = 1;
num++;
--num;
let sum = 0;
let truthyCount = 0;
for (let i = 0; i < array.length; i++) {
let value = array[i];
sum += value;
if (value) {
truthyCount++;
}
}
// хорошо
const array = [1, 2, 3];
let num = 1;
num += 1;
num -= 1;
const sum = array.reduce((a, b) => a + b, 0);
const truthyCount = array.filter(Boolean).length;
```
- [13.7](#variables--linebreak) В присвоении избегайте разрывов строк до и после `=`. Если ваше присвоение нарушает правило [`max-len`](https://eslint.org/docs/rules/max-len.html), оберните значение в круглые скобки. eslint [`operator-linebreak`](https://eslint.org/docs/rules/operator-linebreak.html).
> Почему? Разрывы строк до и после `=` могут приводить к путанице в понимании значения.
```javascript
// плохо
const foo =
superLongLongLongLongLongLongLongLongFunctionName();
// плохо
const foo
= 'superLongLongLongLongLongLongLongLongString';
// хорошо
const foo = (
superLongLongLongLongLongLongLongLongFunctionName()
);
// хорошо
const foo = 'superLongLongLongLongLongLongLongLongString';
```
- [13.8](#variables--no-unused-vars) Запретить неиспользуемые переменные. eslint: [`no-unused-vars`](https://eslint.org/docs/rules/no-unused-vars)
> Почему? Переменные, которые объявлены и не используются в коде, скорее всего, являются ошибкой из-за незавершённого рефакторинга. Такие переменные занимают место в коде и могут привести к путанице при чтении.
```javascript
// плохо
var some_unused_var = 42;
// Переменные, которые используются только для записи, не считаются используемыми.
var y = 10;
y = 5;
// Чтение для собственной модификации.
var z = 0;
z = z + 1;
// Неиспользуемые аргументы функции.
function getX(x, y) {
return x;
}
// хорошо
function getXPlusY(x, y) {
return x + y;
}
var x = 1;
var y = a + 2;
alert(getXPlusY(x, y));
// Переменная 'type' игнорируется, даже если она не испольуется, потому что рядом есть rest-свойство.
// Эта форма извлечения объекта, которая опускает указанные ключи.
var { type, ...coords } = data;
// 'coords' теперь 'data' объект без свойства 'type'.
```
**[⬆ к оглавлению](#Оглавление)**
## Подъём
- [14.1](#hoisting--about) Объявления `var` поднимаются в начало области видимости ближайшей закрывающей их функции, а их присвоение нет. Объявления `const` и `let` работают по новой концепции называемой [Временные Мёртвые Зоны (Temporal Dead Zone)](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Statements/let#временные_мёртвые_зоны_и_ошибки_при_использовании_let). Важно знать, почему использовать [typeof больше не безопасно](https://web.archive.org/web/20200121061528/http://es-discourse.com/t/why-typeof-is-no-longer-safe/15).
```javascript
// мы знаем, что это не будет работать
// (если нет глобальной переменной notDefined)
function example() {
console.log(notDefined); // => выбросит ошибку 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); // => выбросит ошибку ReferenceError
console.log(typeof declaredButNotAssigned); // => выбросит ошибку ReferenceError
const declaredButNotAssigned = true;
}
```
- [14.2](#hoisting--anon-expressions) Для анонимных функциональных выражений наверх области видимости поднимается название переменной, но не её значение.
```javascript
function example() {
console.log(anonymous); // => undefined
anonymous(); // => TypeError anonymous не является функцией
var anonymous = function () {
console.log('anonymous function expression');
};
}
```
- [14.3](#hoisting--named-expressions) Для именованных функциональных выражений наверх области видимости поднимается название переменной, но не имя или тело функции.
```javascript
function example() {
console.log(named); // => undefined
named(); // => TypeError named не является функцией
superPower(); // => ReferenceError superPower не определена
var named = function superPower() {
console.log('Flying');
};
}
// тоже самое справедливо, когда имя функции
// совпадает с именем переменной.
function example() {
console.log(named); // => undefined
named(); // => TypeError named не является функцией
var named = function named() {
console.log('named');
};
}
```
- [14.4](#hoisting--declarations) При объявлении функции её имя и тело поднимаются наверх области видимости.
```javascript
function example() {
superPower(); // => Flying
function superPower() {
console.log('Flying');
}
}
```
- Более подробно можно прочитать в статье [JavaScript Scoping & Hoisting](https://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting/) от [Ben Cherry](https://www.adequatelygood.com/).
**[⬆ к оглавлению](#Оглавление)**
## Операторы сравнения и равенства
- [15.1](#comparison--eqeqeq) Используйте `===` и `!==` вместо `==` и `!=`. eslint: [`eqeqeq`](https://eslint.org/docs/rules/eqeqeq.html)
- [15.2](#comparison--if) Условные операторы, такие как `if`, вычисляются путём приведения к логическому типу `Boolean` через абстрактный метод `ToBoolean` и всегда следуют следующим правилам:
- **Object** соответствует **true**
- **Undefined** соответствует **false**
- **Null** соответствует **false**
- **Boolean** соответствует **значению булева типа**
- **Number** соответствует **false**, если **+0, -0, or NaN**, в остальных случаях **true**
- **String** соответствует **false**, если строка пустая `''`, в остальных случаях **true**
```javascript
if ([0] && []) {
// true
// Массив (даже пустой) является объектом, а объекты возвращают true
}
```
- [15.3](#comparison--shortcuts) Используйте сокращения для булевских типов, а для строк и чисел применяйте явное сравнение.
```javascript
// плохо
if (isValid === true) {
// ...
}
// хорошо
if (isValid) {
// ...
}
// плохо
if (name) {
// ...
}
// хорошо
if (name !== '') {
// ...
}
// плохо
if (collection.length) {
// ...
}
// хорошо
if (collection.length > 0) {
// ...
}
```
- [15.4](#comparison--moreinfo) Более подробную информацию можно узнать в статье [Truth Equality and JavaScript](https://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108) от Angus Croll.
- [15.5](#comparison--switch-blocks) Используйте фигурные скобки для `case` и `default`, если они содержат лексические декларации (например, `let`, `const`, `function`, и `class`). eslint: [`no-case-declarations`](https://eslint.org/docs/rules/no-case-declarations.html).
> Почему? Лексические декларации видны во всем `switch` блоке, но инициализируются только при присваивании, которое происходит при входе в блок `case`. Возникают проблемы, когда множество `case` пытаются определить одно и то же.
```javascript
// плохо
switch (foo) {
case 1:
let x = 1;
break;
case 2:
const y = 2;
break;
case 3:
function f() {
// ...
}
break;
default:
class C {}
}
// хорошо
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.6](#comparison--nested-ternaries) Тернарные операторы не должны быть вложены и в большинстве случаев должны быть расположены на одной строке. eslint: [`no-nested-ternary`](https://eslint.org/docs/rules/no-nested-ternary.html).
```javascript
// плохо
const foo = maybe1 > maybe2
? "bar"
: value1 > value2 ? "baz" : null;
// разбит на два отдельных тернарных выражения
const maybeNull = value1 > value2 ? 'baz' : null;
const foo = maybe1 > maybe2
? 'bar'
: maybeNull;
// отлично
const foo = maybe1 > maybe2 ? 'bar' : maybeNull;
```
- [15.7](#comparison--unneeded-ternary) Избегайте ненужных тернарных операторов. eslint: [`no-unneeded-ternary`](https://eslint.org/docs/rules/no-unneeded-ternary.html).
```javascript
// плохо
const foo = a ? a : b;
const bar = c ? true : false;
const baz = c ? false : true;
// хорошо
const foo = a || b;
const bar = !!c;
const baz = !c;
```
- [15.8](#comparison--no-mixed-operators) При смешивании операторов, помещайте их в круглые скобки. Единственным исключением являются стандартные арифметические операторы: `+`, `-` и `**`, так как их приоритет широко известен. Мы рекомендуем заключить `/` и `*` в круглые скобки, поскольку их приоритет может быть неоднозначным, когда они смешиваются. eslint: [`no-mixed-operators`](https://eslint.org/docs/rules/no-mixed-operators.html)
> Почему? Это улучшает читаемость и уточняет намерения разработчика.
```javascript
// плохо
const foo = a && b < 0 || c > 0 || d + 1 === 0;
// плохо
const bar = a ** b - 5 % d;
// плохо
// можно ошибиться, думая что это (a || b) && c
if (a || b && c) {
return d;
}
// плохо
const bar = a + b / c * d;
// хорошо
const foo = (a && b < 0) || c > 0 || (d + 1 === 0);
// хорошо
const bar = a ** b - (5 % d);
// хорошо
if (a || (b && c)) {
return d;
}
// хорошо
const bar = a + (b / c) * d;
```
**[⬆ к оглавлению](#Оглавление)**
## Блоки
- [16.1](#blocks--braces) Используйте фигурные скобки, когда блок кода занимает несколько строк. eslint: [`nonblock-statement-body-position`](https://eslint.org/docs/rules/nonblock-statement-body-position)
```javascript
// плохо
if (test)
return false;
// хорошо
if (test) return false;
// хорошо
if (test) {
return false;
}
// плохо
function foo() { return false; }
// хорошо
function bar() {
return false;
}
```
- [16.2](#blocks--cuddled-elses) Если блоки кода в условии `if` и `else` занимают несколько строк, расположите оператор `else` на той же строчке, где находится закрывающая фигурная скобка блока `if`. eslint: [`brace-style`](https://eslint.org/docs/rules/brace-style.html)
```javascript
// плохо
if (test) {
thing1();
thing2();
}
else {
thing3();
}
// хорошо
if (test) {
thing1();
thing2();
} else {
thing3();
}
```
- [16.3](#blocks--no-else-return) Если в блоке `if` всегда выполняется оператор `return`, последующий блок `else` не нужен. `return` внутри блока `else if`, следующем за блоком `if`, который содержит `return`, может быть разделён на несколько блоков `if`. eslint: [`no-else-return`](https://eslint.org/docs/rules/no-else-return)
```javascript
// плохо
function foo() {
if (x) {
return x;
} else {
return y;
}
}
// плохо
function cats() {
if (x) {
return x;
} else if (y) {
return y;
}
}
// плохо
function dogs() {
if (x) {
return x;
} else {
if (y) {
return y;
}
}
}
// хорошо
function foo() {
if (x) {
return x;
}
return y;
}
// хорошо
function cats() {
if (x) {
return x;
}
if (y) {
return y;
}
}
// хорошо
function dogs(x) {
if (x) {
if (z) {
return y;
}
} else {
return z;
}
}
```
**[⬆ к оглавлению](#Оглавление)**
- [17.1](#control-statements) Если ваш управляющий оператор (`if`, `while` и т.д.) слишком длинный или превышает максимальную длину строки, то каждое (сгруппированное) условие можно поместить на новую строку. Логический оператор должен располагаться в начале строки.
> Почему? Наличие операторов в начале строки приводит к выравниванию операторов и напоминает цепочку методов. Это также улучшает читаемость, упрощая визуальное отслеживание сложной логики.
```javascript
// плохо
if ((foo === 123 || bar === 'abc') && doesItLookGoodWhenItBecomesThatLong() && isThisReallyHappening()) {
thing1();
}
// плохо
if (foo === 123 &&
bar === 'abc') {
thing1();
}
// плохо
if (foo === 123
&& bar === 'abc') {
thing1();
}
// плохо
if (
foo === 123 &&
bar === 'abc'
) {
thing1();
}
// хорошо
if (
foo === 123
&& bar === 'abc'
) {
thing1();
}
// хорошо
if (
(foo === 123 || bar === 'abc')
&& doesItLookGoodWhenItBecomesThatLong()
&& isThisReallyHappening()
) {
thing1();
}
// хорошо
if (foo === 123 && bar === 'abc') {
thing1();
}
```
- [17.2](#control-statements--value-selection) Не используйте операторы выбора вместо управляющих операторов.
```javascript
// плохо
!isRunning && startRunning();
// хорошо
if (!isRunning) {
startRunning();
}
```
**[⬆ к оглавлению](#Оглавление)**
## Комментарии
- [18.1](#comments--multiline) Используйте конструкцию `/** ... */` для многострочных комментариев.
```javascript
// плохо
// make() возвращает новый элемент
// соответствующий переданному названию тега
//
// @param {String} tag
// @return {Element} element
function make(tag) {
// ...
return element;
}
// хорошо
/**
* make() возвращает новый элемент
* соответствующий переданному названию тега
*/
function make(tag) {
// ...
return element;
}
```
- [18.2](#comments--singleline) Используйте двойной слеш `//` для однострочных комментариев. Располагайте такие комментарии отдельной строкой над кодом, который хотите пояснить. Если комментарий не является первой строкой блока, добавьте сверху пустую строку.
```javascript
// плохо
const active = true; // это текущая вкладка
// хорошо
// это текущая вкладка
const active = true;
// плохо
function getType() {
console.log('fetching type...');
// установить по умолчанию тип 'no type'
const type = this.type || 'no type';
return type;
}
// хорошо
function getType() {
console.log('fetching type...');
// установить по умолчанию тип 'no type'
const type = this.type || 'no type';
return type;
}
// тоже хорошо
function getType() {
// установить по умолчанию тип 'no type'
const type = this.type || 'no type';
return type;
}
```
- [18.3](#comments--spaces) Начинайте все комментарии с пробела, так их проще читать. eslint: [`spaced-comment`](https://eslint.org/docs/rules/spaced-comment)
```javascript
// плохо
//это текущая вкладка
const active = true;
// хорошо
// это текущая вкладка
const active = true;
// плохо
/**
*make() возвращает новый элемент
*соответствующий переданному названию тега
*/
function make(tag) {
// ...
return element;
}
// хорошо
/**
* make() возвращает новый элемент
* соответствующий переданному названию тега
*/
function make(tag) {
// ...
return element;
}
```
- [18.4](#comments--actionitems) Если комментарий начинается со слов `FIXME` или `TODO`, то это помогает другим разработчикам быстро понять, когда вы хотите указать на проблему, которую надо решить, или когда вы предлагаете решение проблемы, которое надо реализовать. Такие комментарии, в отличие от обычных, побуждают к действию: `FIXME: -- нужно разобраться с этим` или `TODO: -- нужно реализовать`.
- [18.5](#comments--fixme) Используйте `// FIXME:`, чтобы описать проблему.
```javascript
class Calculator extends Abacus {
constructor() {
super();
// FIXME: здесь не должна использоваться глобальная переменная
total = 0;
}
}
```
- [18.6](#comments--todo) Используйте `// TODO:`, чтобы описать решение проблемы.
```javascript
class Calculator extends Abacus {
constructor() {
super();
// TODO: нужна возможность задать total через параметры
this.total = 0;
}
}
```
**[⬆ к оглавлению](#Оглавление)**
## Пробелы
- [19.1](#whitespace--spaces) Используйте мягкую табуляцию (символ пробела) шириной в 2 пробела. eslint: [`indent`](https://eslint.org/docs/rules/indent.html)
```javascript
// плохо
function foo() {
∙∙∙∙let name;
}
// плохо
function bar() {
∙let name;
}
// хорошо
function baz() {
∙∙let name;
}
```
- [19.2](#whitespace--before-blocks) Ставьте 1 пробел перед открывающей фигурной скобкой у блока. eslint: [`space-before-blocks`](https://eslint.org/docs/rules/space-before-blocks.html)
```javascript
// плохо
function test(){
console.log('test');
}
// хорошо
function test() {
console.log('test');
}
// плохо
dog.set('attr',{
age: '1 year',
breed: 'Bernese Mountain Dog',
});
// хорошо
dog.set('attr', {
age: '1 year',
breed: 'Bernese Mountain Dog',
});
```
- [19.3](#whitespace--around-keywords) Ставьте 1 пробел перед открывающей круглой скобкой в операторах управления (`if`, `while` и т.п.). Не оставляйте пробелов между списком аргументов и названием в объявлениях и вызовах функций. eslint: [`keyword-spacing`](https://eslint.org/docs/rules/keyword-spacing.html)
```javascript
// плохо
if(isJedi) {
fight ();
}
// хорошо
if (isJedi) {
fight();
}
// плохо
function fight () {
console.log ('Swooosh!');
}
// хорошо
function fight() {
console.log('Swooosh!');
}
```
- [19.4](#whitespace--infix-ops) Разделяйте операторы пробелами. eslint: [`space-infix-ops`](https://eslint.org/docs/rules/space-infix-ops.html)
```javascript
// плохо
const x=y+5;
// хорошо
const x = y + 5;
```
- [19.5](#whitespace--newline-at-end) В конце файла оставляйте одну пустую строку. eslint: [`eol-last`](https://github.com/eslint/eslint/blob/master/docs/rules/eol-last.md)
```javascript
// плохо
import { es6 } from './AirbnbStyleGuide';
// ...
export default es6;
```
```javascript
// плохо
import { es6 } from './AirbnbStyleGuide';
// ...
export default es6;↵
↵
```
```javascript
// хорошо
import { es6 } from './AirbnbStyleGuide';
// ...
export default es6;↵
```
- [19.6](#whitespace--chains) Используйте переносы строк и отступы, когда делаете длинные цепочки методов (больше 2 методов). Ставьте точку в начале строки, чтобы дать понять, что это не новая инструкция, а продолжение цепочки. eslint: [`newline-per-chained-call`](https://eslint.org/docs/rules/newline-per-chained-call) [`no-whitespace-before-property`](https://eslint.org/docs/rules/no-whitespace-before-property)
```javascript
// плохо
$('#items').find('.selected').highlight().end().find('.open').updateCount();
// плохо
$('#items').
find('.selected').
highlight().
end().
find('.open').
updateCount();
// хорошо
$('#items')
.find('.selected')
.highlight()
.end()
.find('.open')
.updateCount();
// плохо
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);
// хорошо
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);
// хорошо
const leds = stage.selectAll('.led').data(data);
const svg = leds.enter().append('svg:svg');
svg.classed('led', true).attr('width', (radius + margin) * 2);
const g = svg.append('svg:g');
g.attr('transform', `translate(${radius + margin},${radius + margin})`).call(tron.led);
```
- [19.7](#whitespace--after-blocks) Оставляйте пустую строку между блоком кода и следующей инструкцией.
```javascript
// плохо
if (foo) {
return bar;
}
return baz;
// хорошо
if (foo) {
return bar;
}
return baz;
// плохо
const obj = {
foo() {
},
bar() {
},
};
return obj;
// хорошо
const obj = {
foo() {
},
bar() {
},
};
return obj;
// плохо
const arr = [
function foo() {
},
function bar() {
},
];
return arr;
// хорошо
const