Skip to content

ivanzusko/javascript

 
 

Repository files navigation

Стиль написання JavaScript від компанії Airbnb

Найбільш обгрунтований підхід до JavaScript

Downloads Downloads Gitter

Інші керівництва по стилю

Зміст

  1. Типи
  2. Посилання
  3. Об'єкти
  4. Масиви
  5. Деструктурування
  6. Рядки
  7. Функції
  8. Arrow-функції
  9. Класи та Конструктори
  10. Модулі
  11. Ітератори та Генератори
  12. Властивості
  13. Змінні
  14. Підняття (Hoisting)
  15. Оператори порівняння і рівності
  16. Блоки
  17. Коментарі
  18. Пробіли
  19. Коми
  20. Крапка з комою
  21. Приведення типів та Примушення (Coercion)
  22. Угоди про іменування
  23. Аксессори
  24. Події
  25. jQuery
  26. ECMAScript 5 сумісність
  27. ECMAScript 6+ (ES 2015+) стилі
  28. Тестування
  29. Продуктивність
  30. Ресурси
  31. В реальному Світі
  32. Переклад
  33. Керівництво зі стилю написання JavaScript
  34. Поговоріть з нами про JavaScript
  35. Автори
  36. 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,
    };

Чому? В загальному, ми вважаємо, що так суб'єктивно легше читати. Це покращує підсвітку синтаксису, а також більш легко оптимізується багатьма 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,
    );

⬆ вверх

Arrow-функції

  • 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, а потім вже групуйте всі lets.

    Чому? Це дуже зручно у випадку, коли в подальшому вам знадобиться оголосити зміну в залежності від вже оголошених змінних.

    // погано
    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;

⬆ вверх

Підняття (Hoisting)

  • 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.1 Використовуйте === та !== а не == і не !=. eslint: eqeqeq

  • 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.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: -- потрібно в цьому розібратись і виправити or TODO: -- потрібно запровадити.

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

⬆ вверх

jQuery

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

⬆ вверх

ECMAScript 5 сумісність

⬆ вверх

ECMAScript 6+ (ES 2015+) стилі

  • 27.1 Це колекція посилання на різні особливості ES6.
  1. Arrow Functions
  2. Класи та Конструктори
  3. Скорочення для методіва об'єкта
  4. Скорочення об'єкта
  5. Вираховані властивості об'єкта
  6. Строчні шаблони
  7. Destructuring
  8. Параметри за замовчуаванням
  9. Rest
  10. spreads оператор масива
  11. Let та Const
  12. Ітератори та Генератори
  13. Модулі

  • 27.2 Не використовуйте TC39 пропозиції які не знаходяться у стадії stage 3.

    Чому? Вони не завершені, і вони можуть бути змінені або повністю відмінені. Ми хочемо використовувати JavaScript, а пропозиції, покищо, ще не JavaScript.

⬆ вверх

Тестування

  • 28.1 Так.

    function foo() {
      return true;
    }

  • 28.2 Ні, але серйозно:
  • Який би тестувальний фреймфорк ви б не використовували - ви повинні писати тести!
  • Намагайтесь писати багато дрібних функцій та зводити до мінімуму місця, де відбуваються мутації.
  • Будьте обережними з stubs та mocks, оскільки вони можуть зробити ваші тести більш крихкими.
  • Ми в першу чергу використовуємо mocha у Airbnb. tape також час від часу використовується для малньких, окремих модулів.
  • 100% покриття тестами - це гарна мета до якої варто прагнути, навіть якщо це не завжди практично.
  • Кожного разу, коли ви виправляєте помилку, пишіть тест регресії. Помилка виправлена без написання регресивного тесту майже точно виникне в майбутньому знову.

⬆ вверх

Продуктивність

⬆ вверх

Ресурси

Вивчення ES6

Прочитайте це

Інструменти

Інші керівництва

Інші стилі

Подальше читання

Книжки

Блоги

Подкасти

⬆ вверх

В реальному Світі

Це перелік організацій які використовують це керівництво. Надішліть нам pull request і ми додамо вас до цього списку.

⬆ вверх

Translation

This style guide is also available in other languages:

The JavaScript Style Guide Guide

Chat With Us About JavaScript

Contributors

License

(The MIT License)

Copyright (c) 2014-2016 Airbnb

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

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

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

⬆ вверх

Amendments

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.

};

About

Translation of Airbnb's JavaScript Style Guide

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • JavaScript 100.0%