Найбільш обгрунтований підхід до JavaScript
Інші керівництва по стилю
- Типи
- Посилання
- Об'єкти
- Масиви
- Деструктурування
- Рядки
- Функції
- Arrow-функції
- Класи та Конструктори
- Модулі
- Ітератори та Генератори
- Властивості
- Змінні
- Підняття (Hoisting)
- Оператори порівняння і рівності
- Блоки
- Коментарі
- Пробіли
- Коми
- Крапка з комою
- Приведення типів та Примушення (Coercion)
- Угоди про іменування
- Аксессори
- Події
- jQuery
- ECMAScript 5 сумісність
- ECMAScript 6+ (ES 2015+) стилі
- Тестування
- Продуктивність
- Ресурси
- В реальному Світі
- Переклад
- Керівництво зі стилю написання JavaScript
- Поговоріть з нами про JavaScript
- Автори
- License
-
1.1 Примітиви: Коли ви отримуєте доступ до примітиву, ви працюєте напряму з його значенням.
string
number
boolean
null
undefined
const foo = 1; let bar = foo; bar = 9; console.log(foo, bar); // => 1, 9
-
1.2 Складні типи: Коли ви отримуєте доступ до складного типу, ви працюєте з посиланням на його значення.
object
array
function
const foo = [1, 2]; const bar = foo; bar[0] = 9; console.log(foo[0], bar[0]); // => 9, 9
-
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
jscs:disallowVar
Чому?
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 Використовуйте літерали (фігурні скобки) для створення нового об'єкта. Не використовуйте для створення нового об'єкта конструктор
new Object
. 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
jscs:requireEnhancedObjectLiterals
// погано 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
jscs:requireEnhancedObjectLiterals
Чому? Так менше писати і більш зрозуміло.
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
jscs:disallowQuotedKeysInObjects
Чому? В загальному, ми вважаємо, що так суб'єктивно легше читати. Це покращує підсвітку синтаксису, а також більш легко оптимізується багатьма 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
.
Чому? Ці методи можуть бути переоприділені на поточному об'єкті, наприклад:
{ hasOwnProperty: false }
, або ж поточний об'єкт може не мати прототипа (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';
…
console.log(has.call(object, key));
- 3.8 Віддавайте перевагу
spread
оператору надObject.assign
для дрібного копіювання об'єктів. Використовуйтеrest
оператор для отримання нового об'єкта з певними відсутніми властивостями.
// дуже погано
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 Використовуйте
...
(spreads
) оператор масива для копіювання масивів.// погано 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);
-
4.5 Використовуйте оператор
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); // погано const flat = {}; [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => { const flatten = memo.concat(item); flat[index] = flatten; }); // добре const flat = {}; [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => { const flatten = memo.concat(item); flat[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; });
-
5.1 Використовуйте деструктурування об'єкта, коли отримуєте доступ і використовуєте декілька властивостей об'єкта. jscs:
requireObjectDestructuring
Чому? Деструктурування вберігає вас від створення тимчасових посиланнь на ті властивості.
// погано 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 Використовуйте деструктурування масивів. jscs:
requireArrayDestructuring
const arr = [1, 2, 3, 4]; // погано const first = arr[0]; const second = arr[1]; // добре const [first, second] = arr;
-
5.3 Використовуйте деструктурування об'єкта, а не масива, для декількох повертаємих значеннь . jscs:
disallowArrayDestructuringReturn
Чому? Ви зможете з часом додати нові властивості або змінити послідовність речей не порушуючи розташування викликів.
// погано 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
jscs:validateQuoteMarks
// погано 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
jscs:requireTemplateStrings
Чому? Рядкові шаблони дають читабельність, короткий синтаксис з переносом нових ліній та функціями інтерполяції рядка.
// погано 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 Ніколи не використовуйте
eval()
на рядку, це відкриває дуже багато вразливостей.
-
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
jscs:disallowFunctionDeclarations
Чому? Функціональні оголошення хойстяться (вспливають уверх), це означає, що дуже легко послатися на функцію до того, як вона оголошена у файлі. Це шкодить читаємості та підтримуємості. Якщо вам здається, що визначення функції досить велике чи воно ускладнює розуміння іншої частини файлу, то, можливо, прийшов час, щоб виокремити це в окремий модуль! Не забувайте іменувати вирази - анонімні функції можуть ускладнити локалізацію проблеми у стеку викликів. (Discussion)
// погано const foo = function () { }; // погано function foo() { } // добре const foo = function bar() { };
-
7.2 Огортайте негайно виконувані функціональні вирази (НВФВ) у дужки. eslint:
wrap-iife
jscs:requireParenthesesAroundIIFE
Чому? Негайно виконуваний функціональний вираз являє собою єдиний блок - огортання обох, і його і його виклику чітко це показує. Варто зауважити, що у світі де модулі повсюди, вам майже ніколи не потрібен НВФВ.
// Негайно виконуваний функціональний вираз (НВФВ) (function () { console.log('Welcome to the Internet. Please follow me.'); }());
- 7.3 Ніколи не оголошуйте функцію у нефункціональному блоці(if, while, etc). Натомість, призначте функцію змінній. Браузери дозволять вам це зробити, але всі вони інтерпретують це по-різному, що є поганими новинами. eslint:
no-loop-func
-
7.4 Увага: ECMA-262 визначає
block
як список визначень. Оголошення функції не є визначенням. Read ECMA-262's note on this issue.// погано if (currentUser) { function test() { console.log('Nope.'); } } // добре let test; if (currentUser) { test = () => { console.log('Yup.'); }; }
-
7.5 Ніколи не називайте параметр як
arguments
. Це матиме пріоритет надarguments
об'єкта, який надається області видимості кожної функції.// погано function nope(name, options, arguments) { // ...щось відбувається... } // добре function yup(name, options, args) { // ...щось відбувається... }
-
7.6 Ніколи не використовуйте
arguments
, краще натомість використовуйтеrest
синтаксис (...
). eslint:prefer-rest-params
Чому?
...
оператор явно зазначає, що ви хочете щось витягти. Крім того, rest аргументи являються реальним масивом, а не масивоподібністю, як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` є неправдивим(`falsy` - прим. прекладача), то воно так і буде задано об'єкту. Це, звісно, може бути тим, що // вам саме потрібно, але це може призвести до тонких багів. 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
Чому? Перепризначення параметрів може призвести до неочікуваної поведінки, особливо, при доступі до об'єкту аргументів. Це також може викликати оптимізаційні проблеми, особливо у 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 Віддавайте перевагу використанню
...
(операторspread
) при виклику функцій зі змінним числом параметрів . 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, 08, 05])); // добре new Date(...[2016, 08, 05]);
-
7.15 Функції з кількома сигнатурами, чи викликами, повинні бути з відступами, так само як і будь-який інший список у кілька рядків у цьому керівництві: з кожним елементом на своєму рядку, з комою у кінці кожного рядка.
// погано function foo(bar, baz, quux) { // тіло функції } // добре function foo( bar, baz, quux, ) { // тіло функції } // погано console.log(foo, bar, baz); // добре console.log( foo, bar, baz, );
-
8.1 Коли вам потрібно використати функціональний вираз (так само якщо потрібно передати анонімну функцію) - використовуйте позначення arrow-функції. eslint:
prefer-arrow-callback
,arrow-spacing
jscs:requireArrowFunctions
Чому? Це створює версію функції, яка виконується у контексті
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
jscs:disallowParenthesesAroundArrowParam
,requireShorthandArrowFunctions
Чому? Синтаксичний цукор. Це гарно читається, особливо коли кілька функцій формують послідовний ланцюжок.
// погано [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 }));
-
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 Якщо ваша функція приймає єдиний аргумент і ви не використовуєте дужки - не використайте в такому разі і фігурні дужки. В іншому випадку, завжди огортайте аргументи дужками. eslint:
arrow-parens
jscs:disallowParenthesesAroundArrowParam
Чому? Менше візуального безладу.
// погано [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 Уникайте синтаксису arrow-функції (
=>
) з операторами порівняння (<=
,>=
), оскільки це може збити з пантелику. 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; };
-
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
) модуля, а не нестандартній модульній системі. Ви завжди можете сконвертувати(transpile) до вашої улюбленої модульної системи.Чому? Модулі - це майбутнє, тож давайте використовувати майбутнє вже зараз.
// погано const AirbnbStyleGuide = require('./AirbnbStyleGuide'); module.exports = AirbnbStyleGuide.es6; // нормально 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 І не експортуйте напряму з імпорту.
Чому? Не дивлячись на те, що одна строка це досить коротко, мати один чіткий шлях для імпорта і один чіткий шлях для експорта робить речі більш зрозумілими.
// погано // filename es6.js export { es6 as default } from './AirbnbStyleGuide'; // добре // filename es6.js import { es6 } from './AirbnbStyleGuide'; export default es6;
-
10.4 Імпортуйте з одного місця лише раз. eslint:
no-duplicate-imports
Чому? Коли є кілька рядків, які імпортують з одного шляху - це ускладнює підтримку коду.
// погано import foo from 'foo'; // … some other imports … // 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 У модулі з єдиним експортом віддавайте превагу експорту за замовчуванням (
default
), а не іменованому експорту. eslint:import/prefer-default-export
// погано export function foo() {} // добре export default function foo() {}
-
10.7 Зазначайте всі
import
визначення над не імпортами. 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 loader
синтаксис у оголошенні модульного імпорту. eslint:import/no-webpack-loader-syntax
Чому? TODO: COMPLETE Since using Webpack syntax in the imports couples the code to a module bundler. Prefer using the loader syntax in
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 Якщо вам потрібно використати генератори, або якщо ви вирішили не скористатись нашою порадою our advice, переконайтесь, що сигнатура функції має правильні відступи. 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
jscs:requireDotNotation
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');
-
13.1 Завжди використовуйте
const
для оголошення змінних. Недотримання цієї вимоги призведе до глобальних змінних. Ми хочемо уникнути забруднення глобального простору імен. Капітан Планета застерігає нас від цього. eslint:no-undef
prefer-const
// погано superPower = new SuperPower(); // добре const superPower = new SuperPower();
-
13.2 Використовуйте по одному
const
для кожної змінної. eslint:one-var
jscs:disallowMultipleVarDecl
Чому? Так легше оголошувати змінні таким чином, що вам не потрібно буде хвилюватись, що ви випадково переплутаєте в кінці рядка
;
з,
. Ви також можете пройти через кожне оголошення змінної за допомогою дебагера, замість того, щоб перестрибнути через всі оголошення змінних одразу.// погано 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
s.Чому? Це дуже зручно у випадку, коли в подальшому вам знадобиться оголосити зміну в залежності від вже оголошених змінних.
// погано 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 Не поєднуйте в ланцюжки присвоєння змінних.
Чому? Поєднання змінних у ланцюжки створює неявні глобальні змінні.
// погано (function example() { // JavaScript інтерпретує це як // let a = ( b = ( c = 1 ) ); // Ключове слово let застосовується до змінної a; змінні b та c стають // глобальними змінними. let a = b = c = 1; }()); console.log(a); // undefined console.log(b); // 1 console.log(c); // 1 // добре (function example() { let a = 1; let b = a; let c = a; }()); console.log(a); // undefined console.log(b); // undefined console.log(c); // undefined // Те ж саме стосується і `const`
-
13.6 Уникайте використання унарних збільшеннь та зменшеннь (++, --). eslint
no-plusplus
Чому? Згідно з документацією eslint, унарні збільшення або зменшення спричиняють автоматичну вставку крапки й коми, що, в свою чергу, може призвести до непомітних помилок при збільшенні або зменшенні значень у рамках програми. Також, більш виразно застосовувати для збільшеннь або зменшень такі вирази як
num += 1
замістьnum++
абоnum ++
. Заборона унарних збільшеннь або зменшеннь також захищає вас від випадкових попередніх збільшеннь/зменшень, які також можуть призвести до непередбачуваної поведінки у ваших програмах.// погано let 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++; } } // добре let 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;
-
14.1 Оголошені змінні, за допомогою ключового слова
var
, піднімаються вгору обсласті видимості функції, в той час як привласнені їм значення - ні. Змінні, оголошені за допомогоюconst
таlet
отримали нову концепцію - Тимчасові Мертві Зони (ТМЗ). Важливо знати, чому використовувати 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 is not a function var anonymous = function () { console.log('anonymous function expression'); }; }
-
14.3 Іменовані функціональні вирази піднімають ім'я змінної, але не ім'я функції чи тіло функції.
function example() { console.log(named); // => undefined named(); // => TypeError named is not a function superPower(); // => ReferenceError superPower is not defined var named = function superPower() { console.log('Flying'); }; } // це також стосується і випадку, // коли ім'я функції співпадає з іменем змінної. function example() { console.log(named); // => undefined named(); // => TypeError named is not a function var named = function named() { console.log('named'); } }
-
14.4 Функціональне оголошення піднімає ім'я і тіло функції.
function example() { superPower(); // => Flying function superPower() { console.log('Flying'); } }
-
За більш детальною інформацією звертайтесь до JavaScript Scoping & Hoisting автор Ben Cherry.
-
15.2 Умовні оператори, такі як
if
вираховують вираз за допомогою примусового приведення до логічного виразуToBoolean
і завжди слідують цим простим правилам:- Objects оцінюється як true
- Undefined оцінюється як false
- Null оцінюється як false
- Booleans оцінюються як the value of the boolean
- Numbers оцінюється як false, якщо +0, -0, or NaN, в усіх інших випадках як true
- Strings оцінюється як false якщо рядок порожній
''
, в усіх інших випадках як true
if ([0] && []) { // true // масив (навіть якщо він порожній) - це об'єкт, а об'єкт завжди оцінюється як true }
-
15.3 Використовуйте скорочення для логічних значеннь, але явно зазначайте, коли порівнюєте рядки та числа.
// погано if (isValid === true) { // ...stuff... } // добре if (isValid) { // ...stuff... } // погано if (name) { // ...stuff... } // добре if (name !== '') { // ...stuff... } // погано if (collection.length) { // ...stuff... } // добре if (collection.length > 0) { // ...stuff... }
- 15.4 Більш детальну інформацію дивіться у статті Truth Equality and JavaScript автора Angus Croll.
- 15.5 Використовуйте дужки для створення блоків
case
таdefault
що містять лексичні декларації (e.g.let
,const
,function
, таclass
).
Чому? Лексичні проголошення видимі у всьому
switch
блоці, але вони ініціалізуються лише тоді, коли привласнюються, а це стається лише тоді, коли спрацьювуєcase
. Це спричиняє проблеми, коли кількаcase
випадків намагаються визначити одну й ту саму річ.
eslint rules: no-case-declarations
.
```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 Тернарні оператори не повинні вкладатись будь яким чином, а мають бути записані в один рядок.
eslint rules:
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 maybeNull = value1 > value2 ? 'baz' : null; const foo = maybe1 > maybe2 ? 'bar' : maybeNull;
-
15.7 Уникайте непотрібних тернарних записів.
eslint rules:
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;
-
16.1 Використовуйте дужки в усіх блоках які записуються у кілька рядків.
// погано 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
jscs:disallowNewlineBeforeBlockStatements
// погано if (test) { thing1(); thing2(); } else { thing3(); } // добре if (test) { thing1(); thing2(); } else { thing3(); }
-
17.1 Використовуйте
/** ... */
для коментарів у кілька рядків.// погано // make() returns a new element // based on the passed in tag name // // @param {String} tag // @return {Element} element function make(tag) { // ...stuff... return element; } // добре /** * make() returns a new element * based on the passed-in tag name */ function make(tag) { // ...stuff... return element; }
-
17.2 Використовуйте
//
для коментарів в один рядок. Ставте однорядковий коментар на новий рядок одразу над суб'єктом, до якого відноситься цей коментар. Ставте порожній рядок перед коментарем, якщо тільки це не перший рядок блоку.// погано const active = true; // is current tab // добре // is current tab const active = true; // погано function getType() { console.log('fetching type...'); // set the default type to 'no type' const type = this._type || 'no type'; return type; } // добре function getType() { console.log('fetching type...'); // set the default type to 'no type' const type = this._type || 'no type'; return type; } // також добре function getType() { // set the default type to 'no type' const type = this._type || 'no type'; return type; }
-
17.3 Починайте всі коментарі з пробілу для більше легкого читання. eslint:
spaced-comment
// погано //is current tab const active = true; // добре // is current tab const active = true; // погано /** *make() returns a new element *based on the passed-in tag name */ function make(tag) { // ...stuff... return element; } // добре /** * make() returns a new element * based on the passed-in tag name */ function make(tag) { // ...stuff... return element; }
- 17.4 Починати ваш коментар зі слів
FIXME
чиTODO
добре, оскільки це допомагає іншим розробникам швидко розуміти, чи ви відзначаєте проблемне місце в коді, яке треба переглянути, чи ви пропонуєте вирішення проблеми, яке має бути запроваджене. Вони відрізняються від звичайних коментарів, оскільки вони вимагають дії. Дія може бутиFIXME: -- потрібно в цьому розібратись і виправити
orTODO: -- потрібно запровадити
.
-
17.5 Використовуйте
// FIXME:
для описання проблеми.class Calculator extends Abacus { constructor() { super(); // FIXME: shouldn't use a global here total = 0; } }
-
17.6 Використовуйте
// TODO:
для описання способів вирішення проблеми.class Calculator extends Abacus { constructor() { super(); // TODO: total should be configurable by an options param this.total = 0; } }
-
18.1 Використовуйте табуляцію у 2 пробіли. eslint:
indent
jscs:validateIndentation
// погано function foo() { ∙∙∙∙const name; } // погано function bar() { ∙const name; } // добре function baz() { ∙∙const name; }
-
18.2 Ставте 1 пробіл перед ведучою фігурною дужкою. eslint:
space-before-blocks
jscs:requireSpaceBeforeBlockStatements
// погано 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', });
-
18.3 Ставте 1 пробіл перед відкриваючою дужкою у умовах (
if
,while
і т.д.). Не ставте пробіли між списком аргументів та іменем функції, та між іменем функції та викликами функції і проголошеннями. eslint:keyword-spacing
jscs:requireSpaceAfterKeywords
// погано if(isJedi) { fight (); } // добре if (isJedi) { fight(); } // погано function fight () { console.log ('Swooosh!'); } // добре function fight() { console.log('Swooosh!'); }
-
18.4 Розмежовуйте оператори пробілами. eslint:
space-infix-ops
jscs:requireSpaceBeforeBinaryOperators
,requireSpaceAfterBinaryOperators
// погано const x=y+5; // добре const x = y + 5;
-
18.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;↵
-
18.6 Використовуйте відступи, коли робите ланцюжки методів (більш ніж два методи у ланцюгу). Використовуйте ведучу крапку, яка підкреслює, що на новій лінії відбувається виклик методу, а не нове ствердження. 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);
-
18.7 Лишайте порожній рядок після блоків і перед наступним ствердженням. jscs:
requirePaddingNewLinesAfterBlocks
// погано 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;
-
18.8 Не насичуйте ваші блоки порожніми лініями. eslint:
padded-blocks
jscs:disallowPaddingNewlinesInBlocks
// погано 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); }
-
18.9 Не додавайте пробілів в середині дужок. eslint:
space-in-parens
jscs:disallowSpacesInsideParentheses
// погано function bar( foo ) { return foo; } // добре function bar(foo) { return foo; } // погано if ( foo ) { console.log(foo); } // добре if (foo) { console.log(foo); }
-
18.10 Не ставте зайвих пробілів в середині квадратних дужок. eslint:
array-bracket-spacing
jscs:disallowSpacesInsideArrayBrackets
// погано const foo = [ 1, 2, 3 ]; console.log(foo[ 0 ]); // добре const foo = [1, 2, 3]; console.log(foo[0]);
-
18.11 Додавайте пробіли в середині фігурних дужок. eslint:
object-curly-spacing
jscs:requireSpacesInsideObjectBrackets
// погано const foo = {clark: 'kent'}; // добре const foo = { clark: 'kent' };
-
18.12 Уникайте ліній коду, що довші за 100 символів (включаючи пробіли). Примітка: зазначені тут довгі рядки не підпадають під це правило і не повинні розбиватись. eslint:
max-len
jscs:maximumLineLength
Чому? Це забезпечує читаємість та підтримку.
// погано 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.1 Направляючі коми: Ні. eslint:
comma-style
jscs:requireCommaBeforeLineBreak
// погано 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', };
-
19.2 Додаткова кома в кінці рядку: Так. eslint:
comma-dangle
jscs:requireTrailingComma
Чому? Це веде до чистіших відмінностей у git. Також, транспайелри, такі як 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 ) { // does nothing } // добре function createHero( firstName, lastName, inventorOf, ) { // does nothing } // добре (зауважте, що кома не повинна з'являтись після "rest" елементу) function createHero( firstName, lastName, inventorOf, ...heroArgs ) { // does nothing } // погано createHero( firstName, lastName, inventorOf ); // добре createHero( firstName, lastName, inventorOf, ); // добре (зауважте, що кома не повинна з'являтись після "rest" елементу) createHero( firstName, lastName, inventorOf, ...heroArgs )
-
20.1 Так. eslint:
semi
jscs:requireSemicolons
// погано (function () { const name = 'Skywalker' return name })() // добре (function () { const name = 'Skywalker'; return name; }()); // добре, але застаріло (захист, щоб функція не перетворювалась на аргумент, коли об'єднуються два файли за допомогою IIFEs(негайно виконуваний функціональний вираз (НВФВ))) ;(() => { const name = 'Skywalker'; return name; }());
- 21.1 Виконуйте примусове приведення типу на початку ствердження.
-
21.2 Рядки:
// => this.reviewScore = 9; // погано const totalScore = this.reviewScore + ''; // викликає this.reviewScore.valueOf() // погано const totalScore = this.reviewScore.toString(); // не гарантовано, що повернеться рядок // добре const totalScore = String(this.reviewScore);
-
21.3 Цифри: Використовуйте
Number
для приведення типу таparseInt
завжди з десятичною для синтаксичного аналізу рядків. eslint:radix
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);
-
21.4 Якщо, з якоїсь причини, ви робите щось дике і
parseInt
являється слабкою ланкою і вам потрібно використати бітову операцію заради ефективності, залиште коментар, який пояснює навіщо і що ви робите.// добре /** * parseInt сповільнював код. * Застосування бітової операції щодо рядка для примусового приведення до * Number робить код набагато швидшим. */ const val = inputValue >> 0;
-
21.5 Зауважте: Будьте обачні при використанні бітових операцій. Цифри представленні як 64-бітні значення, але бітові операції завжди повертають 32-bit ціле число (джерело). Бітова операція може призвести до непердбачуваної поведінки для цілик значеннь, більших ніж 32-біта. Обговорення. Найбільшим виявленим 32-бітним цілим числом є 2,147,483,647:
2147483647 >> 0 //=> 2147483647 2147483648 >> 0 //=> -2147483648 2147483649 >> 0 //=> -2147483647
-
21.6 Булеві значення:
const age = 0; // погано const hasAge = new Boolean(age); // добре const hasAge = Boolean(age); // best const hasAge = !!age;
-
22.1 Уникайте імен в одну літеру. Нехай ваші імена будуть описовими. eslint:
id-length
// погано function q() { // ...stuff... } // добре function query() { // ..stuff.. }
-
22.2 Використовуйте
camelCase
коли називаєте об'єкти, функції і екземпляри. eslint:camelcase
jscs:requireCamelCaseOrUpperCaseIdentifiers
// погано const OBJEcttsssss = {}; const this_is_my_object = {}; function c() {} // добре const thisIsMyObject = {}; function thisIsMyFunction() {}
-
22.3 Використовуйте PascalCase лише коли називаєте конструктори чи класи. eslint:
new-cap
jscs:requireCapitalizedConstructors
// погано 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', });
-
22.4 Не використовуйте завершальних чи лідуючих нижніх підкресленнь(underscores). eslint:
no-underscore-dangle
jscs:disallowDanglingUnderscores
Чому? В JavaScript немає поняття приватності властивостей чи методів. Хоча, лідуюче нижнє підкреслення і прийнято використовувати для позначення "приватності", насправді, ці властивості всі публічні, і тому являються частиною вашого публічного API. Такий підхід може ввести розробниців в оману, що зміна не буде критичною, чи що не потрібні тести. tl;dr: якщо ви хочете зробити щось "приватним", воно не має бути видимим для сторонніх.
// погано this.__firstName__ = 'Panda'; this.firstName_ = 'Panda'; this._firstName = 'Panda'; // добре this.firstName = 'Panda';
-
22.5 Не зберігайте посиланнь на
this
. Використовуйте arrow-функції чи Function#bind. jscs:disallowNodeTypes
// погано 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); }; }
-
22.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" // ^ supports both insideDirectory.js and insideDirectory/index.js
-
22.7 Використовуйте camelCase коли ви експортуєте за замовчуванням function. Ім'я файлу повинно співпадати з іменем функції.
function makeStyleGuide() { } export default makeStyleGuide;
-
22.8 Використовуйте PascalCase коли ви експортуєте конструктор / клас / функціональну бібліотеку / чистий об'єкт.
const AirbnbStyleGuide = { es6: { } }; export default AirbnbStyleGuide;
-
22.9 Скорочення або абревіатури повинні завжди всі писатись або великими або маленькими літерами.
Чому? Імена для зручності читання, а не для вдоволення комп'ютерного алгоритму.
// погано import SmsContainer from './containers/SmsContainer'; // погано const HttpRequests = [ // ... ]; // добре import SMSContainer from './containers/SMSContainer'; // добре const HTTPRequests = [ // ... ]; // найкраще import TextMessageContainer from './containers/TextMessageContainer'; // найкраще const Requests = [ // ... ];
- 23.1 Функції аксессори для доступу до властивостей не потрібні.
-
23.2 Не використовуйте геттери/сеттери JavaScript оскільки вони викликають неочікуванні побічні ефекти і їх важко тестувати, підтримувати і аргументувати їхню необхідність. Натомість, якщо ви робити функцію доступу - використовуйте getVal() та setVal('hello').
// погано class Dragon { get age() { // ... } set age(value) { // ... } } // добре class Dragon { getAge() { // ... } setAge(value) { // ... } }
-
23.3 Якщо властивість/метод являються
boolean
, використовуйтеisVal()
абоhasVal()
.// погано if (!dragon.age()) { return false; } // добре if (!dragon.hasAge()) { return false; }
-
23.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]; } }
-
24.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 });
-
25.1 Префіксуйте об'єкт jQuery знаком
$
. jscs:requireDollarBeforejQueryAssignment
// погано const sidebar = $('.sidebar'); // добре const $sidebar = $('.sidebar'); // добре const $sidebarBtn = $('.sidebar-btn');
-
25.2 Кешуйте результати пошуку jQuery.
// погано function setSidebar() { $('.sidebar').hide(); // ...щось відбувається... $('.sidebar').css({ 'background-color': 'pink' }); } // добре function setSidebar() { const $sidebar = $('.sidebar'); $sidebar.hide(); // ...щось відбувається... $sidebar.css({ 'background-color': 'pink' }); }
- 25.3 Для звернень до DOM використовуйте каскадність запиту
$('.sidebar ul')
або предок > нащадок$('.sidebar > ul')
. jsPerf
-
25.4 Використовуйте
find
з контекстними запитами jQuery об'єкта.// погано $('ul', '.sidebar').hide(); // погано $('.sidebar').find('ul').hide(); // добре $('.sidebar ul').hide(); // добре $('.sidebar > ul').hide(); // добре $sidebar.find('ul').hide();
- 26.1 Звертайтесь до ES5 таблиці сумісності Kangax'са.
- 27.1 Це колекція посилання на різні особливості ES6.
- Arrow Functions
- Класи та Конструктори
- Скорочення для методіва об'єкта
- Скорочення об'єкта
- Вираховані властивості об'єкта
- Строчні шаблони
- Destructuring
- Параметри за замовчуаванням
- Rest
- spreads оператор масива
- Let та Const
- Ітератори та Генератори
- Модулі
-
27.2 Не використовуйте TC39 пропозиції які не знаходяться у стадії stage 3.
Чому? Вони не завершені, і вони можуть бути змінені або повністю відмінені. Ми хочемо використовувати JavaScript, а пропозиції, покищо, ще не JavaScript.
-
28.1 Так.
function foo() { return true; }
- 28.2 Ні, але серйозно:
- Який би тестувальний фреймфорк ви б не використовували - ви повинні писати тести!
- Намагайтесь писати багато дрібних функцій та зводити до мінімуму місця, де відбуваються мутації.
- Будьте обережними з stubs та mocks, оскільки вони можуть зробити ваші тести більш крихкими.
- Ми в першу чергу використовуємо
mocha
у 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? - Loading...
Вивчення ES6
- Draft ECMA 2015 (ES6) Spec
- ExploringJS
- ES6 Compatibility Table
- Comprehensive Overview of ES6 Features
Прочитайте це
Інструменти
- Code Style Linters
Інші керівництва
- Google JavaScript Style Guide
- jQuery Core Style Guidelines
- Principles of Writing Consistent, Idiomatic JavaScript
Інші стилі
- 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
- Dustin Diaz
- nettuts
Подкасти
Це перелік організацій які використовують це керівництво. Надішліть нам pull request і ми додамо вас до цього списку.
- 4Catalyzer: 4Catalyzer/javascript
- Aan Zee: AanZee/javascript
- Adult Swim: adult-swim/javascript
- Airbnb: airbnb/javascript
- Apartmint: apartmint/javascript
- Ascribe: ascribe/javascript
- Avalara: avalara/javascript
- Avant: avantcredit/javascript
- BashPros: BashPros/javascript
- Billabong: billabong/javascript
- Bisk: bisk/javascript
- Blendle: blendle/javascript
- Bonhomme: bonhommeparis/javascript
- Brainshark: brainshark/javascript
- Chartboost: ChartBoost/javascript-style-guide
- ComparaOnline: comparaonline/javascript
- Compass Learning: compasslearning/javascript-style-guide
- DailyMotion: dailymotion/javascript
- DoSomething: DoSomething/eslint-config
- Digitpaint digitpaint/javascript
- Ecosia: ecosia/javascript
- Evernote: evernote/javascript-style-guide
- Evolution Gaming: evolution-gaming/javascript
- EvozonJs: evozonjs/javascript
- ExactTarget: ExactTarget/javascript
- Expensify Expensify/Style-Guide
- Flexberry: Flexberry/javascript-style-guide
- Gawker Media: gawkermedia/javascript
- General Electric: GeneralElectric/javascript
- GoodData: gooddata/gdc-js-style
- Grooveshark: grooveshark/javascript
- How About We: howaboutwe/javascript
- Huballin: huballin/javascript
- HubSpot: HubSpot/javascript
- Hyper: hyperoslo/javascript-playbook
- InfoJobs: InfoJobs/JavaScript-Style-Guide
- Intent Media: intentmedia/javascript
- Jam3: Jam3/Javascript-Code-Conventions
- JeopardyBot: kesne/jeopardy-bot
- JSSolutions: JSSolutions/javascript
- KickorStick: kickorstick/javascript
- Kinetica Solutions: kinetica/javascript
- Lonely Planet: lonelyplanet/javascript
- M2GEN: M2GEN/javascript
- Mighty Spring: mightyspring/javascript
- MinnPost: MinnPost/javascript
- MitocGroup: MitocGroup/javascript
- ModCloth: modcloth/javascript
- Money Advice Service: moneyadviceservice/javascript
- Muber: muber/javascript
- National Geographic: natgeo/javascript
- National Park Service: nationalparkservice/javascript
- Nimbl3: nimbl3/javascript
- Nulogy: nulogy/javascript
- Orion Health: orionhealth/javascript
- OutBoxSoft: OutBoxSoft/javascript
- Peerby: Peerby/javascript
- Razorfish: razorfish/javascript-style-guide
- reddit: reddit/styleguide/javascript
- React: /facebook/react/blob/master/CONTRIBUTING.md#style-guide
- REI: reidev/js-style-guide
- Ripple: ripple/javascript-style-guide
- SeekingAlpha: seekingalpha/javascript-style-guide
- Shutterfly: shutterfly/javascript
- Springload: springload/javascript
- StratoDem Analytics: stratodem/javascript
- SteelKiwi Development: steelkiwi/javascript
- StudentSphere: studentsphere/javascript
- SysGarage: sysgarage/javascript-style-guide
- Syzygy Warsaw: syzygypl/javascript
- Target: target/javascript
- TheLadders: TheLadders/javascript
- The Nerdery: thenerdery/javascript-standards
- T4R Technology: T4R-Technology/javascript
- VoxFeed: VoxFeed/javascript-style-guide
- WeBox Studio: weboxstudio/javascript
- Weggo: Weggo/javascript
- Zillow: zillow/javascript
- ZocDoc: ZocDoc/javascript
This style guide is also available in other languages:
- Brazilian Portuguese: armoucar/javascript-style-guide
- Bulgarian: borislavvv/javascript
- Catalan: fpmweb/javascript-style-guide
- Chinese (Simplified): sivan/javascript-style-guide
- Chinese (Traditional): jigsawye/javascript
- French: nmussy/javascript-style-guide
- German: timofurrer/javascript-style-guide
- Italian: sinkswim/javascript-style-guide
- Japanese: mitsuruog/javascript-style-guide
- Korean: tipjs/javascript-style-guide
- Polish: mjurczyk/javascript
- Russian: uprock/javascript
- Spanish: paolocarrasco/javascript-style-guide
- Thai: lvarayut/javascript-style-guide
- Vietnam: hngiang/javascript-style-guide
- Find us on gitter.
(The MIT License)
Copyright (c) 2014-2016 Airbnb
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
We encourage you to fork this guide and change the rules to fit your team's style guide. Below, you may list some amendments to the style guide. This allows you to periodically update your style guide without having to deal with merge conflicts.