** документ в процессе наполнения... но некоторые правила уже можно и нужно использовать***
Наиболее разумный подход к JavaScript от AirBnB, GitHub, & Google
Зачем нам это ¯\(ツ)/¯ ?
Весь код в любой кодовой базе должен выглядеть так, как будто его набрал один человек, независимо от того, сколько человек внесло свой вклад.
Научитесь программировать как Google
- Типы
- Объявление переменных
- Объекты
- Массивы
- Деструктуризация
- Строки
- Функции
- Стрелочные функции
- Классы и конструкторы
- Модули
- Итераторы и генераторы
- Свойства
- Переменные
- Подъём
- Операторы сравнения и равенства
- Блоки
- Управляющие операторы
- Комментарии
- Пробелы
- Запятые
- Точка с запятой
- Приведение типов
- Соглашение об именовании
- Аксессоры
- События
- jQuery
- Поддержка ECMAScript 5
- Возможности ECMAScript 6+ (ES 2015+)
- Стандартная библиотека
- Тестирование
- Производительность
- Ресурсы
-
1.1 Простые типы: Когда вы взаимодействуете с простым типом, вы напрямую работаете с его значением.
string
number
boolean
null
undefined
symbol
const foo = 1; let bar = foo; bar = 9; console.log(foo, bar); // => 1, 9
- Символы не могут быть полностью заполифиллены, поэтому они не должны использоваться, если разработка ведётся для браузеров/сред, которые не поддерживают их нативно.
-
1.2 Сложные типы: Когда вы взаимодействуете со сложным типом, вы работаете со ссылкой на его значение.
object
array
function
const foo = [1, 2]; const bar = foo; bar[0] = 9; console.log(foo[0], bar[0]); // => 9, 9
-
2.1 Используйте
const
для объявления переменных; избегайтеvar
. eslint:prefer-const
,no-const-assign
Почему? Это гарантирует, что вы не сможете переопределять значения, т.к. это может привести к ошибкам и к усложнению понимания кода.
// плохо var a = 1; var b = 2; // хорошо const a = 1; const b = 2;
-
2.2 Если вам необходимо переопределять значения, то используйте
let
вместоvar
. eslint:no-var
Почему? Область видимости
let
— блок, уvar
— функция.// плохо var count = 1; if (true) { count += 1; } // хорошо, используйте 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
-
3.1 Для создания объекта используйте литеральную нотацию. eslint:
no-new-object
// плохо const item = new Object(); // хорошо const item = {};
-
3.2 Используйте вычисляемые имена свойств, когда создаёте объекты с динамическими именами свойств.
Почему? Они позволяют вам определить все свойства объекта в одном месте.
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 Используйте сокращённую запись метода объекта. eslint:
object-shorthand
// плохо const atom = { value: 1, addValue: function (value) { return atom.value + value; }, }; // хорошо const atom = { value: 1, addValue(value) { return atom.value + value; }, };
-
3.4 Используйте сокращённую запись свойств объекта. eslint:
object-shorthand
Почему? Это короче и понятнее.
const lukeSkywalker = 'Luke Skywalker'; // плохо const obj = { lukeSkywalker: lukeSkywalker, }; // хорошо const obj = { lukeSkywalker, };
-
3.5 Группируйте ваши сокращённые записи свойств в начале объявления объекта.
Почему? Так легче сказать, какие свойства используют сокращённую запись.
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 Только недопустимые идентификаторы помещаются в кавычки. eslint:
quote-props
Почему? На наш взгляд, такой код легче читать. Это улучшает подсветку синтаксиса, а также облегчает оптимизацию для многих JS-движков.
// плохо const bad = { 'foo': 3, 'bar': 4, 'data-blah': 5, }; // хорошо const good = { foo: 3, bar: 4, 'data-blah': 5, };
-
3.7 Не вызывайте напрямую методы
Object.prototype
, такие какhasOwnProperty
,propertyIsEnumerable
, иisPrototypeOf
. eslint:no-prototype-builtins
Почему? Эти методы могут быть переопределены в свойствах объекта, который мы проверяем
{ hasOwnProperty: false }
, или этот объект может бытьnull
(Object.create(null)
).// плохо console.log(object.hasOwnProperty(key)); // хорошо console.log(Object.prototype.hasOwnProperty.call(object, key)); // отлично const has = Object.prototype.hasOwnProperty; // Кэшируем запрос в рамках модуля. /* или */ import has from 'has'; // https://www.npmjs.com/package/has // ... console.log(has.call(object, key));
-
3.8 Используйте оператор расширения вместо
Object.assign
для поверхностного копирования объектов. Используйте синтаксис оставшихся свойств, чтобы получить новый объект с некоторыми опущенными свойствами.// очень плохо 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 Для создания массива используйте литеральную нотацию. eslint:
no-array-constructor
// плохо const items = new Array(); // хорошо const items = [];
-
4.2 Для добавления элемента в массив используйте Array#push вместо прямого присваивания.
const someStack = []; // плохо someStack[someStack.length] = 'abracadabra'; // хорошо someStack.push('abracadabra');
-
4.3 Для копирования массивов используйте оператор расширения
...
.// плохо const len = items.length; const itemsCopy = []; let i; for (i = 0; i < len; i += 1) { itemsCopy[i] = items[i]; } // хорошо const itemsCopy = [...items];
-
4.4 Для преобразования итерируемого объекта в массив используйте оператор расширения
...
вместоArray.from
.const foo = document.querySelectorAll('.foo'); // хорошо const nodes = Array.from(foo); // отлично const nodes = [...foo];
-
4.5 Используйте
Array.from
для преобразования массивоподобного объекта в массив.const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 }; // плохо const arr = Array.prototype.slice.call(arrLike); // хорошо const arr = Array.from(arrLike);
-
4.6 Используйте
Array.from
вместо оператора расширения...
для маппинга итерируемых объектов, это позволяет избежать создания промежуточного массива.// плохо const baz = [...foo].map(bar); // хорошо const baz = Array.from(foo, bar);
-
4.7 Используйте операторы
return
внутри функций обратного вызова в методах массива. Можно опуститьreturn
, когда тело функции состоит из одной инструкции, возвращающей выражение без побочных эффектов. 8.2. eslint:array-callback-return
// хорошо [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); acc[index] = flatten; }); // хорошо [[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => { const flatten = acc.concat(item); acc[index] = flatten; 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 Если массив располагается на нескольких строках, то используйте разрывы строк после открытия и перед закрытием скобок.
// плохо 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 При обращении к нескольким свойствам объекта используйте деструктуризацию объекта. eslint:
prefer-destructuring
Почему? Деструктуризация избавляет вас от создания временных переменных для этих свойств.
// плохо 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 Используйте деструктуризацию массивов. eslint:
prefer-destructuring
const arr = [1, 2, 3, 4]; // плохо const first = arr[0]; const second = arr[1]; // хорошо const [first, second] = arr;
-
5.3 Используйте деструктуризацию объекта для множества возвращаемых значений, но не делайте тоже самое с массивами.
Почему? Вы сможете добавить новые свойства через некоторое время или изменить порядок без последствий.
// плохо 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 Используйте одинарные кавычки
''
для строк. eslint:quotes
// плохо const name = "Capt. Janeway"; // плохо - литерал шаблонной строки должен содержать интерполяцию или переводы строк const name = `Capt. Janeway`; // хорошо const name = 'Capt. Janeway';
-
6.2 Строки, у которых в строчке содержится более 100 символов, не пишутся на нескольких строчках с использованием конкатенации.
Почему? Работать с разбитыми строками неудобно и это затрудняет поиск по коду.
// плохо 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 При создании строки программным путём используйте шаблонные строки вместо конкатенации. eslint:
prefer-template
template-curly-spacing
Почему? Шаблонные строки дают вам читабельность, лаконичный синтаксис с правильными символами перевода строк и функции интерполяции строки.
// плохо 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.5 Не используйте в строках необязательные экранирующие символы. eslint:
no-useless-escape
Почему? Обратные слеши ухудшают читабельность, поэтому они должны быть только при необходимости.
// плохо const foo = '\'this\' \i\s \"quoted\"'; // хорошо const foo = '\'this\' is "quoted"'; const foo = `my name is '${name}'`;
-
7.1 Используйте функциональные выражения вместо объявлений функций. eslint:
func-style
Почему? У объявлений функций есть подъём. Это означает, что можно использовать функцию до того, как она определена в файле, но это вредит читабельности и поддержке. Если вы обнаружили, что определение функции настолько большое или сложное, что мешает понимать остальную часть файла, то, возможно, пришло время извлечь его в отдельный модуль. Не забудьте явно назвать функциональное выражение, независимо от того, подразумевается ли имя из содержащейся переменной (такое часто бывает в современных браузерах или при использовании компиляторов, таких как Babel). Это помогает точнее определять место ошибки по стеку вызовов. (Обсуждение)
// плохо function foo() { // ... } // плохо const foo = function () { // ... }; // хорошо // лексическое имя, отличное от вызываемой(-ых) переменной(-ых) const foo = function uniqueMoreDescriptiveLexicalFoo() { // ... };
-
7.2 Оборачивайте в скобки немедленно вызываемые функции. eslint:
wrap-iife
Почему? Немедленно вызываемая функция представляет собой единый блок. Чтобы чётко показать это — оберните функцию и вызывающие скобки в ещё одни скобки. Обратите внимание, что в мире с модулями вам больше не нужны немедленно вызываемые функции.
// Немедленно вызываемая функция (function () { console.log('Welcome to the Internet. Please follow me.'); }());
- 7.3 Никогда не объявляйте функции в нефункциональном блоке (
if
,while
, и т.д.). Вместо этого присвойте функцию переменной. Браузеры позволяют выполнить ваш код, но все они интерпретируют его по-разному. eslint:no-loop-func
-
7.4 Примечание: ECMA-262 определяет
блок
как список инструкций. Объявление функции не является инструкцией.// плохо if (currentUser) { function test() { console.log('Nope.'); } } // хорошо let test; if (currentUser) { test = () => { console.log('Yup.'); }; }
-
7.5 Никогда не называйте параметр
arguments
. Он будет иметь приоритет над объектомarguments
, который доступен для каждой функции.// плохо function foo(name, options, arguments) { // ... } // хорошо function foo(name, options, args) { // ... }
-
7.6 Никогда не используйте
arguments
, вместо этого используйте синтаксис оставшихся параметров...
. eslint:prefer-rest-params
Почему?
...
явно говорит о том, какие именно аргументы вы хотите извлечь. Кроме того, такой синтаксис создаёт настоящий массив, а не массивоподобный объект какarguments
.// плохо function concatenateAll() { const args = Array.prototype.slice.call(arguments); return args.join(''); } // хорошо function concatenateAll(...args) { return args.join(''); }
-
7.7 Используйте синтаксис записи аргументов по умолчанию, а не изменяйте аргументы функции.
// очень плохо function handleThings(opts) { // Нет! Мы не должны изменять аргументы функции. // Плохо вдвойне: если переменная opts будет ложной, // то ей присвоится пустой объект, а не то что вы хотели. // Это приведёт к коварным ошибкам. opts = opts || {}; // ... } // всё ещё плохо function handleThings(opts) { if (opts === void 0) { opts = {}; } // ... } // хорошо function handleThings(opts = {}) { // ... }
-
7.8 Избегайте побочных эффектов с параметрами по умолчанию.
Почему? И так всё понятно.
var b = 1; // плохо function count(a = b++) { console.log(a); } count(); // 1 count(); // 2 count(3); // 3 count(); // 3
-
7.9 Всегда вставляйте последними параметры по умолчанию.
// плохо function handleThings(opts = {}, name) { // ... } // хорошо function handleThings(name, opts = {}) { // ... }
-
7.10 Никогда не используйте конструктор функций для создания новых функий. eslint:
no-new-func
Почему? Создание функции в таком духе вычисляет строку подобно
eval()
, из-за чего открываются уязвимости.// плохо var add = new Function('a', 'b', 'return a + b'); // всё ещё плохо var subtract = Function('a', 'b', 'return a - b');
-
7.11 Отступы при определении функции. eslint:
space-before-function-paren
space-before-blocks
Почему? Однородность кода — это хорошо. Вам не надо будет добавлять или удалять пробел при манипуляции с именем.
// плохо const f = function(){}; const g = function (){}; const h = function() {}; // хорошо const x = function () {}; const y = function a() {};
-
7.12 Никогда не изменяйте параметры. eslint:
no-param-reassign
Почему? Манипуляция объектами, приходящими в качестве параметров, может вызывать нежелательные побочные эффекты в вызывающей функции.
// плохо function f1(obj) { obj.key = 1; } // хорошо function f2(obj) { const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1; }
-
7.13 Никогда не переназначайте параметры. eslint:
no-param-reassign
Почему? Переназначенные параметры могут привести к неожиданному поведению, особенно при обращении к
arguments
. Это также может вызывать проблемы оптимизации, особенно в V8.// плохо 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 Отдавайте предпочтение использованию оператора расширения
...
при вызове вариативной функции. eslint:prefer-spread
Почему? Это чище, вам не нужно предоставлять контекст, и не так просто составить
new
сapply
.// плохо 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 Функции с многострочным определением или запуском должны содержать такие же отступы, как и другие многострочные списки в этом руководстве: с каждым элементом на отдельной строке, с запятой в конце элемента. eslint:
function-paren-newline
// плохо function foo(bar, baz, quux) { // ... } // хорошо function foo( bar, baz, quux, ) { // ... } // плохо console.log(foo, bar, baz); // хорошо console.log( foo, bar, baz, );
-
8.1 Когда вам необходимо использовать анонимную функцию (например, при передаче встроенной функции обратного вызова), используйте стрелочную функцию. eslint:
prefer-arrow-callback
,arrow-spacing
Почему? Таким образом создаётся функция, которая выполняется в контексте
this
, который мы обычно хотим, а также это более короткий синтаксис.Почему бы и нет? Если у вас есть довольно сложная функция, вы можете переместить эту логику внутрь её собственного именованного функционального выражения.
// плохо [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 Если тело функции состоит из одного оператора, возвращающего выражение без побочных эффектов, то опустите фигурные скобки и используйте неявное возвращение. В противном случае, сохраните фигурные скобки и используйте оператор
return
. eslint:arrow-parens
,arrow-body-style
Почему? Синтаксический сахар. Когда несколько функций соединены вместе, то это лучше читается.
// плохо [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, 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 В случае, если выражение располагается на нескольких строках, то необходимо обернуть его в скобки для лучшей читаемости.
Почему? Это чётко показывает, где функция начинается и где заканчивается.
// плохо ['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call( httpMagicObjectWithAVeryLongName, httpMethod, ) ); // хорошо ['get', 'post', 'put'].map(httpMethod => ( Object.prototype.hasOwnProperty.call( httpMagicObjectWithAVeryLongName, httpMethod, ) ));
-
8.4 Если ваша функция принимает один аргумент и не использует фигурные скобки, то опустите круглые скобки. В противном случае, всегда оборачивайте аргументы круглыми скобками для ясности и последовательности. Примечание: также допускается всегда использовать круглые скобки, в этом случае используйте вариант "always" для eslint. eslint:
arrow-parens
Почему? Меньше визуального беспорядка.
// плохо [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(x => { const y = x + 1; return x * y; }); // хорошо [1, 2, 3].map((x) => { const y = x + 1; return x * y; });
-
8.5 Избегайте схожести стрелочной функции (
=>
) с операторами сравнения (<=
,>=
). eslint:no-confusing-arrow
// плохо 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 Зафиксируйте расположение тела стрелочной функции с неявным возвратом. eslint:
implicit-arrow-linebreak
// плохо foo => bar; foo => (bar); // хорошо foo => bar; foo => (bar); foo => ( bar )
-
9.1 Всегда используйте
class
. Избегайте прямых манипуляций сprototype
.Почему? Синтаксис
class
является кратким и понятным.// плохо 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 Используйте
extends
для наследования.Почему? Это встроенный способ наследовать функциональность прототипа, не нарушая
instanceof
.// плохо 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 Методы могут возвращать
this
, чтобы делать цепочки вызовов.// плохо 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 Вы можете определить свой собственный метод
toString()
, просто убедитесь, что он успешно работает и не создаёт никаких побочных эффектов.class Jedi { constructor(options = {}) { this.name = options.name || 'no name'; } getName() { return this.name; } toString() { return `Jedi - ${this.getName()}`; } }
-
9.5 У классов есть конструктор по умолчанию, если он не задан явно. Можно опустить пустой конструктор или конструктор, который только делегирует выполнение родительскому классу. eslint:
no-useless-constructor
// плохо 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 Избегайте дублирующих членов класса. eslint:
no-dupe-class-members
Почему? Если объявление члена класса повторяется, без предупреждения будет использовано последнее. Наличие дубликатов скорее всего приведёт к ошибке.
// плохо class Foo { bar() { return 1; } bar() { return 2; } } // хорошо class Foo { bar() { return 1; } } // хорошо class Foo { bar() { return 2; } }
-
10.1 Всегда используйте модули (
import
/export
) вместо нестандартных модульных систем. Вы всегда сможете транспилировать код в вашу любимую модульную систему.Почему? Модули — это будущее. Давайте начнём использовать будущее уже сейчас!
// плохо 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 Не используйте импорт через
*
.Почему? Это гарантирует, что у вас есть единственный экспорт по умолчанию.
// плохо import * as AirbnbStyleGuide from './AirbnbStyleGuide'; // хорошо import AirbnbStyleGuide from './AirbnbStyleGuide';
-
10.3 Не экспортируйте прямо из импорта.
Почему? Несмотря на то, что запись в одну строку является краткой, разделение на отдельные строки делает вещи последовательными.
// плохо // файл es6.js export { es6 as default } from './AirbnbStyleGuide'; // хорошо // файл es6.js import { es6 } from './AirbnbStyleGuide'; export default es6;
-
10.4 Импортируйте из пути только один раз. eslint:
no-duplicate-imports
Почему? Наличие нескольких строк, которые импортируют из одного и того же файла, может сделать код неподдерживаемым.
// плохо import foo from 'foo'; // … какие-то другие импорты … // import { named1, named2 } from 'foo'; // хорошо import foo, { named1, named2 } from 'foo'; // хорошо import foo, { named1, named2, } from 'foo';
-
10.5 Не экспортируйте изменяемые переменные. eslint:
import/no-mutable-exports
Почему? Вообще, следует избегать мутации, в особенности при экспорте изменяемых переменных. Несмотря на то, что эта техника может быть необходима в редких случаях, в основном только константа должна быть экспортирована.
// плохо let foo = 3; export { foo }; // хорошо const foo = 3; export { foo };
-
10.6 В модулях с единственным экспортом предпочтительнее использовать экспорт по умолчанию, а не экспорт по имени. eslint:
import/prefer-default-export
Почему? Для того чтобы поощрять создание множества файлов, которые бы экспортировали одну сущность, т.к. это лучше для читабельности и поддержки кода.
// плохо export function foo() {} // хорошо export default function foo() {}
-
10.7 Поместите все импорты выше остальных инструкций. eslint:
import/first
Почему? Так как
import
обладает подъёмом, то хранение их всех в начале файла предотвращает от неожиданного поведения.// плохо import foo from 'foo'; foo.init(); import bar from 'bar'; // хорошо import foo from 'foo'; import bar from 'bar'; foo.init();
-
10.8 Импорты на нескольких строках должны быть с отступами как у многострочных литералов массива и объекта.
Почему? Фигурные скобки следуют тем же правилам отступа как и любая другая фигурная скобка блока в этом руководстве, тоже самое касается висячих запятых.
// плохо import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path'; // хорошо import { longNameA, longNameB, longNameC, longNameD, longNameE, } from 'path';
-
10.9 Запретите синтаксис загрузчика Webpack в импорте. eslint:
import/no-webpack-loader-syntax
Почему? Использование Webpack синтаксиса связывает код с упаковщиком модулей. Предпочтительно использовать синтаксис загрузчика в
webpack.config.js
.// плохо import fooSass from 'css!sass!foo.scss'; import barCss from 'style!css!bar.css'; // хорошо import fooSass from 'foo.scss'; import barCss from 'bar.css';
-
11.1 Не используйте итераторы. Применяйте функции высшего порядка вместо таких циклов как
for-in
илиfor-of
. eslint:no-iterator
no-restricted-syntax
Почему? Это обеспечивает соблюдение нашего правила о неизменности переменных. Работать с чистыми функциями, которые возвращают значение, проще, чем с функциями с побочными эффектами.
Используйте
map()
/every()
/filter()
/find()
/findIndex()
/reduce()
/some()
/ ... для итерации по массивам, аObject.keys()
/Object.values()
/Object.entries()
для создания массивов, с помощью которых можно итерироваться по объектам.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 Не используйте пока генераторы.
Почему? Они не очень хорошо транспилируются в ES5.
-
11.3 Если всё-таки необходимо использовать генераторы, или вы не обратили внимания на наш совет, убедитесь, что
*
у функции генератора расположена должным образом. eslint:generator-star-spacing
Почему?
function
и*
являются частью одного и того же ключевого слова.*
не является модификатором дляfunction
,function*
является уникальной конструкцией, отличной отfunction
.// плохо 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 Используйте точечную нотацию для доступа к свойствам. eslint:
dot-notation
const luke = { jedi: true, age: 28, }; // плохо const isJedi = luke['jedi']; // хорошо const isJedi = luke.jedi;
-
12.2 Используйте скобочную нотацию
[]
, когда название свойства хранится в переменной.const luke = { jedi: true, age: 28, }; function getProp(prop) { return luke[prop]; } const isJedi = getProp('jedi');
-
12.3 Используйте оператор
**
для возведения в степень. eslint:no-restricted-properties
.// плохо const binary = Math.pow(2, 10); // хорошо const binary = 2 ** 10;
-
13.1 Всегда используйте
const
илиlet
для объявления переменных. Невыполнение этого требования приведёт к появлению глобальных переменных. Необходимо избегать загрязнения глобального пространства имён. eslint:no-undef
prefer-const
// плохо superPower = new SuperPower(); // хорошо const superPower = new SuperPower();
-
13.2 Используйте объявление
const
илиlet
для каждой переменной или присвоения. eslint:one-var
Почему? Таким образом проще добавить новые переменные. Также вы никогда не будете беспокоиться о перемещении
;
и,
и об отображении изменений в пунктуации. Вы также можете пройтись по каждому объявлению с помощью отладчика, вместо того, чтобы прыгать через все сразу.// плохо 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 В первую очередь группируйте
const
, а затемlet
.Почему? Это полезно, когда в будущем вам понадобится создать переменную, зависимую от предыдущих.
// плохо 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 Создавайте переменные там, где они вам необходимы, но помещайте их в подходящее место.
Почему?
let
иconst
имеют блочную область видимости, а не функциональную.// плохо - вызов ненужной функции 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 Не создавайте цепочки присваивания переменных. eslint:
no-multi-assign
Почему? Такие цепочки создают неявные глобальные переменные.
// плохо (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 Избегайте использования унарных инкрементов и декрементов (
++
,--
). eslintno-plusplus
Почему? Согласно документации eslint, унарные инкремент и декремент автоматически вставляют точку с запятой, что может стать причиной трудноуловимых ошибок при инкрементировании и декрементировании значений. Также нагляднее изменять ваши значения таким образом
num += 1
вместоnum++
илиnum ++
. Запрет на унарные инкремент и декремент ограждает вас от непреднамеренных преждевременных инкрементаций/декрементаций значений, которые могут привести к непредсказуемому поведению вашей программы.// плохо 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 В присвоении избегайте разрывов строк до и после
=
. Если ваше присвоение нарушает правилоmax-len
, оберните значение в круглые скобки. eslintoperator-linebreak
.Почему? Разрывы строк до и после
=
могут приводить к путанице в понимании значения.// плохо const foo = superLongLongLongLongLongLongLongLongFunctionName(); // плохо const foo = 'superLongLongLongLongLongLongLongLongString'; // хорошо const foo = ( superLongLongLongLongLongLongLongLongFunctionName() ); // хорошо const foo = 'superLongLongLongLongLongLongLongLongString';
-
13.8 Запретить неиспользуемые переменные. eslint:
no-unused-vars
Почему? Переменные, которые объявлены и не используются в коде, скорее всего, являются ошибкой из-за незавершённого рефакторинга. Такие переменные занимают место в коде и могут привести к путанице при чтении.
// плохо 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 Объявления
var
поднимаются в начало области видимости ближайшей закрывающей их функции, а их присвоение нет. Объявленияconst
иlet
работают по новой концепции называемой Временные Мёртвые Зоны (Temporal Dead Zone). Важно знать, почему использовать typeof больше не безопасно.// мы знаем, что это не будет работать // (если нет глобальной переменной 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 Для анонимных функциональных выражений наверх области видимости поднимается название переменной, но не её значение.
function example() { console.log(anonymous); // => undefined anonymous(); // => TypeError anonymous не является функцией var anonymous = function () { console.log('anonymous function expression'); }; }
-
14.3 Для именованных функциональных выражений наверх области видимости поднимается название переменной, но не имя или тело функции.
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 При объявлении функции её имя и тело поднимаются наверх области видимости.
function example() { superPower(); // => Flying function superPower() { console.log('Flying'); } }
-
Более подробно можно прочитать в статье JavaScript Scoping & Hoisting от Ben Cherry.
-
15.2 Условные операторы, такие как
if
, вычисляются путём приведения к логическому типуBoolean
через абстрактный методToBoolean
и всегда следуют следующим правилам:- Object соответствует true
- Undefined соответствует false
- Null соответствует false
- Boolean соответствует значению булева типа
- Number соответствует false, если +0, -0, or NaN, в остальных случаях true
- String соответствует false, если строка пустая
''
, в остальных случаях true
if ([0] && []) { // true // Массив (даже пустой) является объектом, а объекты возвращают true }
-
15.3 Используйте сокращения для булевских типов, а для строк и чисел применяйте явное сравнение.
// плохо if (isValid === true) { // ... } // хорошо if (isValid) { // ... } // плохо if (name) { // ... } // хорошо if (name !== '') { // ... } // плохо if (collection.length) { // ... } // хорошо if (collection.length > 0) { // ... }
- 15.4 Более подробную информацию можно узнать в статье Truth Equality and JavaScript от Angus Croll.
-
15.5 Используйте фигурные скобки для
case
иdefault
, если они содержат лексические декларации (например,let
,const
,function
, иclass
). eslint:no-case-declarations
.Почему? Лексические декларации видны во всем
switch
блоке, но инициализируются только при присваивании, которое происходит при входе в блокcase
. Возникают проблемы, когда множествоcase
пытаются определить одно и то же.// плохо 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 Тернарные операторы не должны быть вложены и в большинстве случаев должны быть расположены на одной строке. eslint:
no-nested-ternary
.// плохо 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 Избегайте ненужных тернарных операторов. eslint:
no-unneeded-ternary
.// плохо 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 При смешивании операторов, помещайте их в круглые скобки. Единственное исключение — это стандартные арифметические операторы (
+
,-
,*
и/
), так как их приоритет широко известен. eslint:no-mixed-operators
Почему? Это улучшает читаемость и уточняет намерения разработчика.
// плохо 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 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 Используйте фигурные скобки, когда блок кода занимает несколько строк. eslint:
nonblock-statement-body-position
// плохо if (test) return false; // хорошо if (test) return false; // хорошо if (test) { return false; } // плохо function foo() { return false; } // хорошо function bar() { return false; }
-
16.2 Если блоки кода в условии
if
иelse
занимают несколько строк, расположите операторelse
на той же строчке, где находится закрывающая фигурная скобка блокаif
. eslint:brace-style
// плохо if (test) { thing1(); thing2(); } else { thing3(); } // хорошо if (test) { thing1(); thing2(); } else { thing3(); }
-
16.3 Если в блоке
if
всегда выполняется операторreturn
, последующий блокelse
не нужен.return
внутри блокаelse if
, следующем за блокомif
, который содержитreturn
, может быть разделён на несколько блоковif
. eslint:no-else-return
// плохо 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 Если ваш управляющий оператор (
if
,while
и т.д.) слишком длинный или превышает максимальную длину строки, то каждое (сгруппированное) условие можно поместить на новую строку. Логический оператор должен располагаться в начале строки.Почему? Наличие операторов в начале строки приводит к выравниванию операторов и напоминает цепочку методов. Это также улучшает читаемость, упрощая визуальное отслеживание сложной логики.
// плохо 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 Не используйте операторы выбора вместо управляющих операторов.
// плохо !isRunning && startRunning(); // хорошо if (!isRunning) { startRunning(); }
-
18.1 Используйте конструкцию
/** ... */
для многострочных комментариев.// плохо // make() возвращает новый элемент // соответствующий переданному названию тега // // @param {String} tag // @return {Element} element function make(tag) { // ... return element; } // хорошо /** * make() возвращает новый элемент * соответствующий переданному названию тега */ function make(tag) { // ... return element; }
-
18.2 Используйте двойной слеш
//
для однострочных комментариев. Располагайте такие комментарии отдельной строкой над кодом, который хотите пояснить. Если комментарий не является первой строкой блока, добавьте сверху пустую строку.// плохо 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 Начинайте все комментарии с пробела, так их проще читать. eslint:
spaced-comment
// плохо //это текущая вкладка const active = true; // хорошо // это текущая вкладка const active = true; // плохо /** *make() возвращает новый элемент *соответствующий переданному названию тега */ function make(tag) { // ... return element; } // хорошо /** * make() возвращает новый элемент * соответствующий переданному названию тега */ function make(tag) { // ... return element; }
- 18.4 Если комментарий начинается со слов
FIXME
илиTODO
, то это помогает другим разработчикам быстро понять, когда вы хотите указать на проблему, которую надо решить, или когда вы предлагаете решение проблемы, которое надо реализовать. Такие комментарии, в отличие от обычных, побуждают к действию:FIXME: -- нужно разобраться с этим
илиTODO: -- нужно реализовать
.
-
18.5 Используйте
// FIXME:
, чтобы описать проблему.class Calculator extends Abacus { constructor() { super(); // FIXME: здесь не должна использоваться глобальная переменная total = 0; } }
-
18.6 Используйте
// TODO:
, чтобы описать решение проблемы.class Calculator extends Abacus { constructor() { super(); // TODO: нужна возможность задать total через параметры this.total = 0; } }
-
19.1 Используйте мягкую табуляцию (символ пробела) шириной в 2 пробела. eslint:
indent
// плохо function foo() { ∙∙∙∙let name; } // плохо function bar() { ∙let name; } // хорошо function baz() { ∙∙let name; }
-
19.2 Ставьте 1 пробел перед открывающей фигурной скобкой у блока. eslint:
space-before-blocks
// плохо 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 Ставьте 1 пробел перед открывающей круглой скобкой в операторах управления (
if
,while
и т.п.). Не оставляйте пробелов между списком аргументов и названием в объявлениях и вызовах функций. eslint:keyword-spacing
// плохо if(isJedi) { fight (); } // хорошо if (isJedi) { fight(); } // плохо function fight () { console.log ('Swooosh!'); } // хорошо function fight() { console.log('Swooosh!'); }
-
19.4 Разделяйте операторы пробелами. eslint:
space-infix-ops
// плохо const x=y+5; // хорошо const x = y + 5;
-
19.5 В конце файла оставляйте одну пустую строку. eslint:
eol-last
// плохо import { es6 } from './AirbnbStyleGuide'; // ... export default es6;
// плохо import { es6 } from './AirbnbStyleGuide'; // ... export default es6;↵ ↵
// хорошо import { es6 } from './AirbnbStyleGuide'; // ... export default es6;↵
-
19.6 Используйте переносы строк и отступы, когда делаете длинные цепочки методов (больше 2 методов). Ставьте точку в начале строки, чтобы дать понять, что это не новая инструкция, а продолжение цепочки. eslint:
newline-per-chained-call
no-whitespace-before-property
// плохо $('#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);
-
19.7 Оставляйте пустую строку между блоком кода и следующей инструкцией.
// плохо 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 arr = [ function foo() { }, function bar() { }, ]; return arr;
-
19.8 Не добавляйте отступы до или после кода внутри блока. eslint:
padded-blocks
// плохо function bar() { console.log(foo); } // тоже плохо if (baz) { console.log(qux); } else { console.log(foo); } // хорошо function bar() { console.log(foo); } // хорошо if (baz) { console.log(qux); } else { console.log(foo); }
-
19.9 Не добавляйте пробелы между круглыми скобками и их содержимым. eslint:
space-in-parens
// плохо function bar( foo ) { return foo; } // хорошо function bar(foo) { return foo; } // плохо if ( foo ) { console.log(foo); } // хорошо if (foo) { console.log(foo); }
-
19.10 Не добавляйте пробелы между квадратными скобками и их содержимым. eslint:
array-bracket-spacing
// плохо const foo = [ 1, 2, 3 ]; console.log(foo[ 0 ]); // хорошо const foo = [1, 2, 3]; console.log(foo[0]);
-
19.11 Добавляйте пробелы между фигурными скобками и их содержимым. eslint:
object-curly-spacing
// плохо const foo = {clark: 'kent'}; // хорошо const foo = { clark: 'kent' };
-
19.12 Старайтесь не допускать, чтобы строки были длиннее 100 символов (включая пробелы). Замечание: согласно пункту выше, длинные строки с текстом освобождаются от этого правила и не должны разбиваться на несколько строк. eslint:
max-len
Почему? Это обеспечивает удобство чтения и поддержки кода.
// плохо const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy; // плохо $.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.')); // хорошо const foo = jsonData && jsonData.foo && jsonData.foo.bar && jsonData.foo.bar.baz && jsonData.foo.bar.baz.quux && jsonData.foo.bar.baz.quux.xyzzy; // хорошо $.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' }, }) .done(() => console.log('Congratulations!')) .fail(() => console.log('You have failed this city.'));
-
19.13 Требуйте согласованного расстояния между открывающим символом блока и следующим символом на одной и той же строке. Тоже самое касается расстояния между закрывающим символом блока и предыдущим символом. eslint:
block-spacing
// плохо function foo() {return true;} if (foo) { bar = 0;} // хорошо function foo() { return true; } if (foo) { bar = 0; }
-
19.14 Избегайте пробелов перед запятыми и ставьте его после. eslint:
comma-spacing
// плохо var foo = 1,bar = 2; var arr = [1 , 2]; // хорошо var foo = 1, bar = 2; var arr = [1, 2];
-
19.15 Избегайте пробелов внутри скобок вычисляемого свойства. eslint:
computed-property-spacing
// плохо obj[foo ] obj[ 'foo'] var x = {[ b ]: a} obj[foo[ bar ]] // хорошо obj[foo] obj['foo'] var x = { [b]: a } obj[foo[bar]]
-
19.16 Избегайте пробелов между функциями и их вызовами. eslint:
func-call-spacing
// плохо func (); func (); // хорошо func();
-
19.17 Обеспечьте согласованное расстояние между ключами и значениями в свойствах литералов объекта. eslint:
key-spacing
// плохо var obj = { "foo" : 42 }; var obj2 = { "foo":42 }; // хорошо var obj = { "foo": 42 };
- 19.18 Избегайте пробелов в конце строки. eslint:
no-trailing-spaces
-
19.19 Избегайте множества пустых строк и делайте только одну пустую строку в конце файла. eslint:
no-multiple-empty-lines
// плохо var x = 1; var y = 2; // хорошо var x = 1; var y = 2;
-
20.1 Не начинайте строку с запятой. eslint:
comma-style
// плохо const story = [ once , upon , aTime ]; // хорошо const story = [ once, upon, aTime, ]; // плохо const hero = { firstName: 'Ada' , lastName: 'Lovelace' , birthYear: 1815 , superPower: 'computers' }; // хорошо const hero = { firstName: 'Ada', lastName: 'Lovelace', birthYear: 1815, superPower: 'computers', };
-
20.2 Добавляйте висячие запятые. eslint:
comma-dangle
Почему? Такой подход даёт понятную разницу при просмотре изменений. Кроме того, транспиляторы типа Babel удалят висячие запятые из собранного кода, поэтому вы можете не беспокоиться о проблемах в старых браузерах.
// плохо - git diff без висячей запятой const hero = { firstName: 'Florence', - lastName: 'Nightingale' + lastName: 'Nightingale', + inventorOf: ['coxcomb chart', 'modern nursing'] }; // хорошо - git diff с висячей запятой const hero = { firstName: 'Florence', lastName: 'Nightingale', + inventorOf: ['coxcomb chart', 'modern nursing'], };
// плохо const hero = { firstName: 'Dana', lastName: 'Scully' }; const heroes = [ 'Batman', 'Superman' ]; // хорошо const hero = { firstName: 'Dana', lastName: 'Scully', }; const heroes = [ 'Batman', 'Superman', ]; // плохо function createHero( firstName, lastName, inventorOf ) { // ничего не делает } // хорошо function createHero( firstName, lastName, inventorOf, ) { // ничего не делает } // хорошо (обратите внимание, что висячей запятой не должно быть после rest-параметра) function createHero( firstName, lastName, inventorOf, ...heroArgs ) { // ничего не делает } // плохо createHero( firstName, lastName, inventorOf ); // хорошо createHero( firstName, lastName, inventorOf, ); // хорошо (обратите внимание, что висячей запятой не должно быть после rest-аргумента) createHero( firstName, lastName, inventorOf, ...heroArgs );
-
Почему? Когда JavaScript встречает перенос строки без точки с запятой, он использует правило под названием Автоматическая Вставка Точки с запятой (Automatic Semicolon Insertion), чтобы определить, стоит ли считать этот перенос строки как конец выражения и (как следует из названия) поместить точку с запятой в вашем коде до переноса строки. Однако, ASI содержит несколько странных форм поведения, и ваш код может быть сломан, если JavaScript неверно истолкует ваш перенос строки. Эти правила станут сложнее, когда новые возможности станут частью JavaScript. Явное завершение ваших выражений и настройка вашего линтера для улавливания пропущенных точек с запятыми помогут вам предотвратить возникновение проблем.
// плохо - выбрасывает исключение const luke = {} const leia = {} [luke, leia].forEach(jedi => jedi.father = 'vader') // плохо - выбрасывает исключение const reaction = "No! That’s impossible!" (async function meanwhileOnTheFalcon() { // переносимся к `leia`, `lando`, `chewie`, `r2`, `c3p0` // ... }()) // плохо - возвращает `undefined` вместо значения на следующей строке. Так всегда происходит, когда `return` расположен сам по себе, потому что ASI (Автоматическая Вставка Точки с запятой)! function foo() { return 'search your feelings, you know it to be foo' } // хорошо const luke = {}; const leia = {}; [luke, leia].forEach((jedi) => { jedi.father = 'vader'; }); // хорошо const reaction = "No! That’s impossible!"; (async function meanwhileOnTheFalcon() { // переносимся к `leia`, `lando`, `chewie`, `r2`, `c3p0` // ... }()); // хорошо function foo() { return 'search your feelings, you know it to be foo'; }
- 22.1 Выполняйте приведение типов в начале инструкции.
-
22.2 Строки: eslint:
no-new-wrappers
// => this.reviewScore = 9; // плохо const totalScore = new String(this.reviewScore); // тип totalScore будет "object", а не "string" // плохо const totalScore = this.reviewScore + ''; // вызывает this.reviewScore.valueOf() // плохо const totalScore = this.reviewScore.toString(); // нет гарантии что вернётся строка // хорошо const totalScore = String(this.reviewScore);
-
22.3 Числа: Используйте
Number
иparseInt
с основанием системы счисления. eslint:radix
no-new-wrappers
const inputValue = '4'; // плохо const val = new Number(inputValue); // плохо const val = +inputValue; // плохо const val = inputValue >> 0; // плохо const val = parseInt(inputValue); // хорошо const val = Number(inputValue); // хорошо const val = parseInt(inputValue, 10);
-
22.4 Если по какой-то причине вы делаете что-то настолько безумное, что
parseInt
является слабым местом и вам нужно использовать побитовый сдвиг из-за вопросов производительности, оставьте комментарий, объясняющий почему и что вы делаете.// хорошо /** * этот код медленно работал из-за parseInt. * побитовый сдвиг строки для приведения её к числу * работает значительно быстрее. */ const val = inputValue >> 0;
-
22.5 Примечание: Будьте осторожны с побитовыми операциями. Числа в JavaScript являются 64-битными значениями, но побитовые операции всегда возвращают 32-битные значения (источник). Побитовый сдвиг может привести к неожиданному поведению для значений больше, чем 32 бита. Обсуждение. Верхний предел — 2 147 483 647:
2147483647 >> 0; // => 2147483647 2147483648 >> 0; // => -2147483648 2147483649 >> 0; // => -2147483647
-
22.6 Логические типы: eslint:
no-new-wrappers
const age = 0; // плохо const hasAge = new Boolean(age); // хорошо const hasAge = Boolean(age); // отлично const hasAge = !!age;
-
23.1 Избегайте названий из одной буквы. Имя должно быть наглядным. eslint:
id-length
// плохо function q() { // ... } // хорошо function query() { // ... }
-
23.2 Используйте
camelCase
для именования объектов, функций и экземпляров. eslint:camelcase
// плохо const OBJEcttsssss = {}; const this_is_my_object = {}; function c() {} // хорошо const thisIsMyObject = {}; function thisIsMyFunction() {}
-
23.3 Используйте
PascalCase
только для именования конструкторов и классов. eslint:new-cap
// плохо function user(options) { this.name = options.name; } const bad = new user({ name: 'nope', }); // хорошо class User { constructor(options) { this.name = options.name; } } const good = new User({ name: 'yup', });
-
23.4 Не используйте
_
в начале или в конце названий. eslint:no-underscore-dangle
Почему? JavaScript не имеет концепции приватности свойств или методов. Хотя подчёркивание в начале имени является распространённым соглашением, которое показывает «приватность», фактически эти свойства являются такими же доступными, как и часть вашего публичного API. Это соглашение может привести к тому, что разработчики будут ошибочно думать, что изменения не приведут к поломке или что тесты не нужны. Итог: если вы хотите, чтобы что-то было «приватным», то оно не должно быть доступно извне.
// плохо this.__firstName__ = 'Panda'; this.firstName_ = 'Panda'; this._firstName = 'Panda'; // хорошо this.firstName = 'Panda'; // хорошо, в средах, где поддерживается WeakMaps // смотрите https://kangax.github.io/compat-table/es6/#test-WeakMap const firstNames = new WeakMap(); firstNames.set(this, 'Panda');
-
23.5 Не сохраняйте ссылку на
this
. Используйте стрелочные функции или метод bind().// плохо function foo() { const self = this; return function () { console.log(self); }; } // плохо function foo() { const that = this; return function () { console.log(that); }; } // хорошо function foo() { return () => { console.log(this); }; }
-
23.6 Название файла точно должно совпадать с именем его экспорта по умолчанию.
// содержание файла 1 class CheckBox { // ... } export default CheckBox; // содержание файла 2 export default function fortyTwo() { return 42; } // содержание файла 3 export default function insideDirectory() {} // в других файлах // плохо import CheckBox from './checkBox'; // PascalCase import/export, camelCase filename import FortyTwo from './FortyTwo'; // PascalCase import/filename, camelCase export import InsideDirectory from './InsideDirectory'; // PascalCase import/filename, camelCase export // плохо import CheckBox from './check_box'; // PascalCase import/export, snake_case filename import forty_two from './forty_two'; // snake_case import/filename, camelCase export import inside_directory from './inside_directory'; // snake_case import, camelCase export import index from './inside_directory/index'; // requiring the index file explicitly import insideDirectory from './insideDirectory/index'; // requiring the index file explicitly // хорошо import CheckBox from './CheckBox'; // PascalCase export/import/filename import fortyTwo from './fortyTwo'; // camelCase export/import/filename import insideDirectory from './insideDirectory'; // camelCase export/import/directory name/implicit "index" // ^ поддерживает оба варианта: insideDirectory.js и insideDirectory/index.js
- 23.7 Используйте
camelCase
, когда экспортируете функцию по умолчанию. Ваш файл должен называться так же, как и имя функции.function makeStyleGuide() { // ... } export default makeStyleGuide;
-
23.8 Используйте
PascalCase
, когда экспортируете конструктор / класс / синглтон / библиотечную функцию / объект.const AirbnbStyleGuide = { es6: { }, }; export default AirbnbStyleGuide;
-
23.9 Сокращения или буквенные аббревиатуры всегда должны быть в верхнем или нижнем регистре.
Почему? Имена предназначены для удобства чтения.
// плохо import SmsContainer from './containers/SmsContainer'; // плохо const HttpRequests = [ // ... ]; // хорошо import SMSContainer from './containers/SMSContainer'; // хорошо const HTTPRequests = [ // ... ]; // также хорошо const httpRequests = [ // ... ]; // отлично import TextMessageContainer from './containers/TextMessageContainer'; // отлично const requests = [ // ... ];
- 24.1 Функции-аксессоры для свойств объекта больше не нужны.
-
24.2 Не используйте геттеры/сеттеры, т.к. они вызывают неожиданные побочные эффекты, а также их тяжело тестировать, поддерживать и понимать. Вместо этого создавайте методы
getVal()
иsetVal('hello')
.// плохо class Dragon { get age() { // ... } set age(value) { // ... } } // хорошо class Dragon { getAge() { // ... } setAge(value) { // ... } }
-
24.3 Если свойство/метод возвращает логический тип, то используйте названия
isVal()
илиhasVal()
.// плохо if (!dragon.age()) { return false; } // хорошо if (!dragon.hasAge()) { return false; }
-
24.4 Можно создавать функции
get()
иset()
, но нужно быть последовательным.class Jedi { constructor(options = {}) { const lightsaber = options.lightsaber || 'blue'; this.set('lightsaber', lightsaber); } set(key, val) { this[key] = val; } get(key) { return this[key]; } }
-
25.1 Когда привязываете данные к событию (например, события
DOM
или какие-то собственные события, какBackbone
события), передавайте литерал объекта (также известный как «хэш») вместо простого значения. Это позволяет другим разработчикам добавлять больше данных без поиска и изменения каждого обработчика события. К примеру, вместо:// плохо $(this).trigger('listingUpdated', listing.id); // ... $(this).on('listingUpdated', (e, listingID) => { // делает что-то с listingID });
предпочитайте:
// хорошо $(this).trigger('listingUpdated', { listingID: listing.id }); // ... $(this).on('listingUpdated', (e, data) => { // делает что-то с data.listingID });
-
26.1 Начинайте названия переменных, хранящих объект jQuery, со знака
$
.// плохо const sidebar = $('.sidebar'); // хорошо const $sidebar = $('.sidebar'); // хорошо const $sidebarBtn = $('.sidebar-btn');
-
26.2 Кэшируйте jQuery-поиски.
// плохо function setSidebar() { $('.sidebar').hide(); // ... $('.sidebar').css({ 'background-color': 'pink', }); } // хорошо function setSidebar() { const $sidebar = $('.sidebar'); $sidebar.hide(); // ... $sidebar.css({ 'background-color': 'pink', }); }
- 26.3 Для поиска в DOM используйте каскады
$('.sidebar ul')
или селектор родитель > ребёнок$('.sidebar > ul')
. jsPerf
-
26.4 Используйте функцию
find
для поиска в сохранённых jQuery-объектах.// плохо $('ul', '.sidebar').hide(); // плохо $('.sidebar').find('ul').hide(); // хорошо $('.sidebar ul').hide(); // хорошо $('.sidebar > ul').hide(); // хорошо $sidebar.find('ul').hide();
- 27.1 Можно посмотреть в таблице поддержки ES5 от пользователя Kangax .
- 28.1 Здесь собраны ссылки на различные возможности ES6.
- Стрелочные функции
- Классы и конструкторы
- Сокращённая запись методов объекта
- Сокращённая запись свойств объекта
- Вычисляемые имена свойств объекта
- Шаблонные строки
- Деструктуризация
- Параметры по умолчанию
- Оставшиеся параметры
- Оператор расширения
- Let и Const
- Итераторы и генераторы
- Модули
-
28.2 Не используйте предложения TC39, которые не перешли на 3-ю стадию.
Почему? Они ещё не закончены и могут быть изменены или полностью изъяты. Мы хотим использовать JavaScript, а предложения ещё не стали частью JavaScript.
Стандартная библиотека содержит утилиты, функциональность которых сломана, но они остались для поддержки старого кода.
-
29.1 Используйте
Number.isNaN
вместо глобальногоisNaN
. eslint:no-restricted-globals
Почему? Глобальная функция
isNaN
приводит не-числа к числам, возвращаяtrue
для всего, что приводится кNaN
. Если такое поведение необходимо, сделайте его явным.// плохо isNaN('1.2'); // false isNaN('1.2.3'); // true // хорошо Number.isNaN('1.2.3'); // false Number.isNaN(Number('1.2.3')); // true
-
29.2 Используйте
Number.isFinite
вместо глобальногоisFinite
. eslint:no-restricted-globals
Почему? Глобальная функция
isFinite
приводит не-числа к числам, возвращаяtrue
для всего, что приводится к конечному числу. Если такое поведение необходимо, сделайте его явным.// плохо isFinite('2e3'); // true // хорошо Number.isFinite('2e3'); // false Number.isFinite(parseInt('2e3', 10)); // true
-
30.1 Ага.
function foo() { return true; }
- 30.2 Нет, но серьёзно:
- Какой бы фреймворк вы не использовали, вы должны писать тесты!
- Стремитесь к тому, чтобы написать много маленьких чистых функций, и к тому, чтобы свести к минимуму места, где происходят мутации.
- Будьте осторожны со стабами (stubs) и моками (mocks) — они могут сделать ваше тестирование хрупким.
- Мы в первую очередь советуем вам использовать
mocha
иjest
от Airbnb.tape
также иногда используется для небольших, отдельных модулей. - 100% покрытие тестами — это хорошая цель, к которой надо стремиться, даже если это не всегда практично.
- Всякий раз, когда вы исправляете ошибку, пишите регрессионный тест. Исправленная ошибка без регрессионного тестирования почти наверняка всплывёт в будущем.
- On Layout & Web Performance
- String vs Array Concat
- Try/Catch Cost In a Loop
- Bang Function
- jQuery Find vs Context, Selector
- innerHTML vs textContent for script text
- Long String Concatenation
- Are Javascript functions like
map()
,reduce()
, andfilter()
optimized for traversing arrays? - Загрузка...
Изучение ES6+
- Последняя спецификация ECMA
- ExploringJS
- ES6 Compatibility Table
- Comprehensive Overview of ES6 Features
Почитайте это
Инструменты
- Линтеры
- Neutrino Preset - @neutrinojs/airbnb
Другие руководства
- Google JavaScript Style Guide
- jQuery Core Style Guidelines
- Principles of Writing Consistent, Idiomatic JavaScript
- StandardJS
Другие стили
- Naming this in nested functions - Christian Johansen
- Conditional Callbacks - Ross Allen
- Popular JavaScript Coding Conventions on GitHub - JeongHoon Byun
- Multiple var statements in JavaScript, not superfluous - Ben Alman
Дальнейшее чтение
- Understanding JavaScript Closures - Angus Croll
- Basic JavaScript for the impatient programmer - Dr. Axel Rauschmayer
- You Might Not Need jQuery - Zack Bloom & Adam Schwartz
- ES6 Features - Luke Hoban
- Frontend Guidelines - Benjamin De Cock
Книги
- JavaScript: The Good Parts - Douglas Crockford
- JavaScript Patterns - Stoyan Stefanov
- Pro JavaScript Design Patterns - Ross Harmes and Dustin Diaz
- High Performance Web Sites: Essential Knowledge for Front-End Engineers - Steve Souders
- Maintainable JavaScript - Nicholas C. Zakas
- JavaScript Web Applications - Alex MacCaw
- Pro JavaScript Techniques - John Resig
- Smashing Node.js: JavaScript Everywhere - Guillermo Rauch
- Secrets of the JavaScript Ninja - John Resig and Bear Bibeault
- Human JavaScript - Henrik Joreteg
- Superhero.js - Kim Joar Bekkelund, Mads Mobæk, & Olav Bjorkoy
- JSBooks - Julien Bouquillon
- Third Party JavaScript - Ben Vinegar and Anton Kovalyov
- Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript - David Herman
- Eloquent JavaScript - Marijn Haverbeke
- You Don’t Know JS: ES6 & Beyond - Kyle Simpson
Блоги
- JavaScript Weekly
- JavaScript, JavaScript...
- Bocoup Weblog
- Adequately Good
- NCZOnline
- Perfection Kills
- Ben Alman
- Dmitry Baranovskiy
- nettuts
Подкасты