Skip to content

maslenkov/javascript-airbnb

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 

Repository files navigation

Руководство по написанию JavaScript кода от Airbnb() {

Наиболее разумный подход к написанию JavaScript кода

Скачать Скачать Gitter

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

Оглавление

  1. Типы
  2. Объявление переменных
  3. Объекты
  4. Массивы
  5. Деструктуризация
  6. Строки
  7. Функции
  8. Стрелочные функции
  9. Классы и конструкторы
  10. Модули
  11. Итераторы и генераторы
  12. Свойства
  13. Переменные
  14. Подъем
  15. Операторы сравнения и равенства
  16. Блоки
  17. Комментарии
  18. Пробелы
  19. Запятые
  20. Точка с запятой
  21. Приведение типов
  22. Соглашение об именовании
  23. Аксессоры
  24. События
  25. jQuery
  26. Поддержка ECMAScript 5
  27. Возможности ECMAScript 6+ (ES 2015+)
  28. Тестирование
  29. Производительность
  30. Ресурсы
  31. В реальной жизни
  32. Переводы
  33. Пообщаться с разработчиками Airbnb
  34. Участники перевода
  35. Лицензия

  • 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 Для создания объекта используйте литеральную нотацию. 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 }, или этот объект может быть null (Object.create(null)).

    // плохо
    console.log(object.hasOwnProperty(key));
    
    // хорошо
    console.log(Object.prototype.hasOwnProperty.call(object, key));
    
    // отлично
    const has = Object.prototype.hasOwnProperty; // Кэшируем запрос в рамках модуля.
    /* или */
    import has from 'has';
    // ...
    console.log(has.call(object, key));

  • 3.8 Используйте оператор расширения вместо Object.assign для поверхностного копирования объектов. Используйте синтаксис оставшихся свойств, чтобы получить новый объект с некоторыми опущенными свойствами.

    // очень плохо
    const original = { a: 1, b: 2 };
    const copy = Object.assign(original, { c: 3 }); // эта переменная изменяет `original` ಠ_ಠ
    delete copy.a; // если сделать так
    
    // плохо
    const original = { a: 1, b: 2 };
    const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }
    
    // хорошо
    const original = { a: 1, b: 2 };
    const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 }
    
    const { a, ...noA } = copy; // noA => { b: 2, c: 3 }

⬆ к оглавлению

  • 4.1 Для создания массива используйте литеральную нотацию. eslint: no-array-constructor

    // плохо
    const items = new Array();
    
    // хорошо
    const items = [];

  • 4.2 Для добавления элемента в массив используйте Array#push вместо прямого присваивания.

    const someStack = [];
    
    // плохо
    someStack[someStack.length] = 'abracadabra';
    
    // хорошо
    someStack.push('abracadabra');

  • 4.3 Для копирования массивов используйте оператор расширения ....

    // плохо
    const len = items.length;
    const itemsCopy = [];
    let i;
    
    for (i = 0; i < len; i += 1) {
      itemsCopy[i] = items[i];
    }
    
    // хорошо
    const itemsCopy = [...items];

  • 4.4 Чтобы преобразовать массиво-подобный объект в массив, используйте Array.from.

    const foo = document.querySelectorAll('.foo');
    const nodes = Array.from(foo);

  • 4.5 Используйте операторы return внутри функций обратного вызова в методах массива. Можно опустить return, когда тело функции состоит из одной инструкции. 8.2. eslint: array-callback-return

    // хорошо
    [1, 2, 3].map((x) => {
      const y = x + 1;
      return x * y;
    });
    
    // хорошо
    [1, 2, 3].map(x => x + 1);
    
    // плохо
    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;
    });

  • 4.6 Если массив располагается на нескольких строках, то используйте разрывы строк после открытия и перед закрытием скобок.

    // плохо
    const arr = [
      [0, 1], [2, 3], [4, 5],
    ];
    
    const objectInArray = [{
      id: 1,
    }, {
      id: 2,
    }];
    
    const numberInArray = [
      1, 2,
    ];
    
    // хорошо
    const arr = [[0, 1], [2, 3], [4, 5]];
    
    const objectInArray = [
      {
        id: 1,
      },
      {
        id: 2,
      },
    ];
    
    const numberInArray = [
      1,
      2,
    ];

⬆ к оглавлению

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

    Почему? Вы cможете добавить новые свойства через некоторое время или изменить порядок без последствий.

    // плохо
    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

    Почему? У объявлений функций есть подъем. Это означает, что можно использовать функцию до того, как она определена в файле, но это вредит читабельности и поддержке. Если вы обнаружили, что определение функции настолько большое или сложное, что мешает понимать остальную часть файла, то, возможно, пришло время извлечь его в отдельный модуль. Не забывайте называть функциональные выражения — анонимные функции усложняют поиск проблемы в стеке вызовов. (Обсуждение)

    // плохо
    function foo() {
      // ...
    }
    
    // плохо
    const foo = function () {
      // ...
    };
    
    // хорошо
    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, и т.д.). Вместо этого присвойте функцию переменной. Браузеры позволяют выполнить ваш код, но все они интерпретируют его по-разному. eslint: no-loop-func

  • 7.4 Примечание: ECMA-262 определяет блок как список инструкций. Объявление функции не является инструкцией. Подробнее в документе ECMA-262.

    // плохо
    if (currentUser) {
      function test() {
        console.log('Nope.');
      }
    }
    
    // хорошо
    let test;
    if (currentUser) {
      test = () => {
        console.log('Yup.');
      };
    }

  • 7.5 Никогда не называйте параметр arguments. Он будет иметь приоритет над объектом arguments, который доступен для каждой функции.

    // плохо
    function foo(name, options, arguments) {
      // ...
    }
    
    // хорошо
    function foo(name, options, args) {
      // ...
    }

  • 7.6 Никогда не используйте arguments, вместо этого используйте синтаксис оставшихся параметров .... eslint: prefer-rest-params

    Почему? ... явно говорит о том, какие именно аргументы вы хотите извлечь. Кроме того, такой синтаксис создает настоящий массив, а не массиво-подобный объект как arguments.

    // плохо
    function concatenateAll() {
      const args = Array.prototype.slice.call(arguments);
      return args.join('');
    }
    
    // хорошо
    function concatenateAll(...args) {
      return args.join('');
    }

  • 7.7 Используйте синтаксис записи аргументов по умолчанию, а не изменяйте аргументы функции.

    // очень плохо
    function handleThings(opts) {
      // Нет! Мы не должны изменять аргументы функции.
      // Плохо вдвойне: если переменная opts будет ложной,
      // то ей присвоится пустой объект, а не то что вы хотели.
      // Это приведет к коварным ошибкам.
      opts = opts || {};
      // ...
    }
    
    // все еще плохо
    function handleThings(opts) {
      if (opts === void 0) {
        opts = {};
      }
      // ...
    }
    
    // хорошо
    function handleThings(opts = {}) {
      // ...
    }

  • 7.8 Избегайте побочных эффектов с параметрами по умолчанию.

    Почему? И так все понятно.

    var b = 1;
    // плохо
    function count(a = b++) {
      console.log(a);
    }
    count();  // 1
    count();  // 2
    count(3); // 3
    count();  // 3

  • 7.9 Всегда вставляйте последними параметры по умолчанию.

    // плохо
    function handleThings(opts = {}, name) {
      // ...
    }
    
    // хорошо
    function handleThings(name, opts = {}) {
      // ...
    }

  • 7.10 Никогда не используйте конструктор функций для создания новых функий. eslint: no-new-func

    Почему? Создание функции в таком духе вычисляет строку подобно eval(), из-за чего открываются уязвимости.

    // плохо
    var add = new Function('a', 'b', 'return a + b');
    
    // всё ещё плохо
    var subtract = Function('a', 'b', 'return a - b');

  • 7.11 Отступы при определении функции. eslint: space-before-function-paren space-before-blocks

    Почему? Однородность кода — это хорошо. Вам не надо будет добавлять или удалять пробел при манипуляции с именем.

    // плохо
    const f = function(){};
    const g = function (){};
    const h = function() {};
    
    // хорошо
    const x = function () {};
    const y = function a() {};

  • 7.12 Никогда не изменяйте параметры. eslint: no-param-reassign

    Почему? Манипуляция объектами, приходящими в качестве параметров, может вызывать нежелательные побочные эффекты в вызывающей функции.

    // плохо
    function f1(obj) {
      obj.key = 1;
    }
    
    // хорошо
    function f2(obj) {
      const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
    }

  • 7.13 Никогда не переназначайте параметры. eslint: no-param-reassign

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

    // плохо
    function f1(a) {
      a = 1;
      // ...
    }
    
    function f2(a) {
      if (!a) { a = 1; }
      // ...
    }
    
    // хорошо
    function f3(a) {
      const b = a || 1;
      // ...
    }
    
    function f4(a = 1) {
      // ...
    }

  • 7.14 Отдавайте предпочтение использованию оператора расширения ... при вызове вариативной функции. eslint: prefer-spread

    Почему? Это чище, вам не нужно предоставлять контекст, и не так просто составить new с apply.

    // плохо
    const x = [1, 2, 3, 4, 5];
    console.log.apply(console, x);
    
    // хорошо
    const x = [1, 2, 3, 4, 5];
    console.log(...x);
    
    // плохо
    new (Function.prototype.bind.apply(Date, [null, 2016, 8, 5]));
    
    // хорошо
    new Date(...[2016, 8, 5]);

  • 7.15 Функции с многострочным определением или запуском должны содержать такие же отступы, как и другие многострочные списки в этом руководстве: с каждым элементом на отдельной строке, с запятой в конце элемента.

    // плохо
    function foo(bar,
                 baz,
                 quux) {
      // ...
    }
    
    // хорошо
    function foo(
      bar,
      baz,
      quux,
    ) {
      // ...
    }
    
    // плохо
    console.log(foo,
      bar,
      baz);
    
    // хорошо
    console.log(
      foo,
      bar,
      baz,
    );

⬆ к оглавлению

  • 8.1 Когда вам необходимо использовать функциональное выражение (например, анонимную функцию), используйте стрелочную функцию. eslint: prefer-arrow-callback, arrow-spacing 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 Если ваша функция принимает один аргумент и не использует фигурные скобки, то опустите круглые скобки. В противном случае, всегда оборачивайте аргументы круглыми скобками для ясности и последовательности. Примечание: также допускается всегда использовать круглые скобки, в этом случае используйте вариант "always" для eslint или не включайте disallowParenthesesAroundArrowParam для jscs. 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 Избегайте схожести стрелочной функции (=>) с операторами сравнения (<=, >=). 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) вместо нестандартных модульных систем. Вы всегда сможете транспилировать код в вашу любимую модульную систему.

    Почему? Модули — это будущее. Давайте начнем использовать будущее уже сейчас!

    // плохо
    const AirbnbStyleGuide = require('./AirbnbStyleGuide');
    module.exports = AirbnbStyleGuide.es6;
    
    // ok
    import AirbnbStyleGuide from './AirbnbStyleGuide';
    export default AirbnbStyleGuide.es6;
    
    // отлично
    import { es6 } from './AirbnbStyleGuide';
    export default es6;

  • 10.2 Не используйте импорт через *.

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

    // плохо
    import * as AirbnbStyleGuide from './AirbnbStyleGuide';
    
    // хорошо
    import AirbnbStyleGuide from './AirbnbStyleGuide';

  • 10.3 Не экспортируйте прямо из импорта.

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

    // плохо
    // файл es6.js
    export { es6 as default } from './AirbnbStyleGuide';
    
    // хорошо
    // файл es6.js
    import { es6 } from './AirbnbStyleGuide';
    export default es6;

  • 10.4 Импортируйте из пути только один раз. eslint: no-duplicate-imports

    Почему? Наличие нескольких строк, которые импортируют из одного и того же файла, может сделать код неподдерживаемым.

    // плохо
    import foo from 'foo';
    // … какие-то другие импорты … //
    import { named1, named2 } from 'foo';
    
    // хорошо
    import foo, { named1, named2 } from 'foo';
    
    // хорошо
    import foo, {
      named1,
      named2,
    } from 'foo';

  • 10.5 Не экспортируйте изменяемые переменные. eslint: import/no-mutable-exports

    Почему? Вообще, следует избегать мутации, в особенности при экспорте изменяемых переменных. Несмотря на то, что эта техника может быть необходима в редких случаях, в основном только константа должна быть экспортирована.

    // плохо
    let foo = 3;
    export { foo };
    
    // хорошо
    const foo = 3;
    export { foo };

  • 10.6 В модулях с единственным экспортом предпочтительнее использовать экспорт по умолчанию, а не экспорт по имени. eslint: import/prefer-default-export

    // плохо
    export function foo() {}
    
    // хорошо
    export default function foo() {}

  • 10.7 Поместите все импорты выше остальных инструкций. eslint: import/first

    Почему? Так как import обладает подъемом, то хранение их всех в начале файла предотвращает от неожиданного поведения.

    // плохо
    import foo from 'foo';
    foo.init();
    
    import bar from 'bar';
    
    // хорошо
    import foo from 'foo';
    import bar from 'bar';
    
    foo.init();

  • 10.8 Импорты на нескольких строках должны быть с отступами как у многострочных литералов массива и объекта.

    Почему? Фигурные скобки следуют тем же правилам отступа как и любая другая фигурная скобка блока в этом руководстве, тоже самое касается висячих запятых.

    // плохо
    import {longNameA, longNameB, longNameC, longNameD, longNameE} from 'path';
    
    // хорошо
    import {
      longNameA,
      longNameB,
      longNameC,
      longNameD,
      longNameE,
    } from 'path';

  • 10.9 Запретите синтаксис загрузчика Webpack в импорте. eslint: import/no-webpack-loader-syntax

    Почему? Использование Webpack синтаксиса связывает код с упаковщиком модулей. Предпочтительно использовать синтаксис загрузчика в webpack.config.js.

    // плохо
    import fooSass from 'css!sass!foo.scss';
    import barCss from 'style!css!bar.css';
    
    // хорошо
    import fooSass from 'foo.scss';
    import barCss from 'bar.css';

⬆ к оглавлению

  • 11.1 Не используйте итераторы. Применяйте функции высшего порядка вместо таких циклов как for-in или for-of. eslint: no-iterator no-restricted-syntax

    Почему? Это обеспечивает соблюдение нашего правила о неизменности переменных. Работать с чистыми функциями, которые возвращают значение, проще, чем с функциями с побочными эффектами.

    Используйте map() / every() / filter() / find() / findIndex() / reduce() / some() / ... для итерации по массивам, а Object.keys() / Object.values() / Object.entries() для создания массивов, с помощью которых можно итерироваться по объектам.

    const numbers = [1, 2, 3, 4, 5];
    
    // плохо
    let sum = 0;
    for (let num of numbers) {
      sum += num;
    }
    sum === 15;
    
    // хорошо
    let sum = 0;
    numbers.forEach(num => sum += num);
    sum === 15;
    
    // отлично (используйте силу функций)
    const sum = numbers.reduce((total, num) => total + num, 0);
    sum === 15;
    
    // плохо
    const increasedByOne = [];
    for (let i = 0; i < numbers.length; i++) {
      increasedByOne.push(numbers[i] + 1);
    }
    
    // хорошо
    const increasedByOne = [];
    numbers.forEach(num => increasedByOne.push(num + 1));
    
    // отлично (продолжайте в том же духе)
    const increasedByOne = numbers.map(num => num + 1);

  • 11.2 Не используйте пока генераторы.

    Почему? Они не очень хорошо транспилируются в ES5.

  • 11.3 Если все-таки необходимо использовать генераторы, или вы не обратили внимания на наш совет, убедитесь, что * у функции генератора расположена должным образом. eslint: generator-star-spacing

    Почему? function и * являются частью одного и того же ключевого слова. * не является модификатором для function, function* является уникальной конструкцией, отличной от function.

    // плохо
    function * foo() {
      // ...
    }
    
    const bar = function * () {
      // ...
    };
    
    const baz = function *() {
      // ...
    };
    
    const quux = function*() {
      // ...
    };
    
    function*foo() {
      // ...
    }
    
    function *foo() {
      // ...
    }
    
    // очень плохо
    function
    *
    foo() {
      // ...
    }
    
    const wat = function
    *
    () {
      // ...
    };
    
    // хорошо
    function* foo() {
      // ...
    }
    
    const foo = function* () {
      // ...
    };

⬆ к оглавлению

  • 12.1 Используйте точечную нотацию для доступа к свойствам. eslint: dot-notation 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 или let для объявления переменных. Невыполнение этого требования приведет к появлению глобальных переменных. Необходимо избегать загрязнения глобального пространства имен. eslint: no-undef prefer-const

    // плохо
    superPower = new SuperPower();
    
    // хорошо
    const superPower = new SuperPower();

  • 13.2 Используйте объявление const или let для каждой переменной. 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.

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

    // плохо
    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 ++. Запрет на унарные инкремент и декремент ограждает вас от непреднамеренных преждевременных инкрементаций/декрементаций значений, которые могут привести к непредсказуемому поведению вашей программы.

    // плохо
    
    const array = [1, 2, 3];
    let num = 1;
    num++;
    --num;
    
    let sum = 0;
    let truthyCount = 0;
    for (let i = 0; i < array.length; i++) {
      let value = array[i];
      sum += value;
      if (value) {
        truthyCount++;
      }
    }
    
    // хорошо
    
    const array = [1, 2, 3];
    let num = 1;
    num += 1;
    num -= 1;
    
    const sum = array.reduce((a, b) => a + b, 0);
    const truthyCount = array.filter(Boolean).length;

⬆ к оглавлению

  • 14.1 Объявления var поднимаются к началу своей области видимости, а их присвоение нет. Объявления const и let работают по новой концепции называемой Временные Мертвые Зоны (Temporal Dead Zone). Важно знать, почему использовать typeof больше не безопасно.

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

  • 14.2 Для анонимных функциональных выражений наверх области видимости поднимается название переменной, но не ее значение.

    function example() {
      console.log(anonymous); // => undefined
    
      anonymous(); // => TypeError anonymous не является функцией
    
      var anonymous = function () {
        console.log('anonymous function expression');
      };
    }

  • 14.3 Для именованных функциональных выражений наверх области видимости поднимается название переменной, но не имя или тело функции.

    function example() {
      console.log(named); // => undefined
    
      named(); // => TypeError named не является функцией
    
      superPower(); // => ReferenceError superPower не определена
    
      var named = function superPower() {
        console.log('Flying');
      };
    }
    
    // тоже самое справедливо, когда имя функции
    // совпадает с именем переменной.
    function example() {
      console.log(named); // => undefined
    
      named(); // => TypeError named не является функцией
    
      var named = function named() {
        console.log('named');
      };
    }

  • 14.4 При объявлении функции ее имя и тело поднимаются наверх области видимости.

    function example() {
      superPower(); // => Flying
    
      function superPower() {
        console.log('Flying');
      }
    }
  • Более подробно можно прочитать в статье JavaScript Scoping & Hoisting от Ben Cherry.

⬆ к оглавлению

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

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

    • Object соответствует true
    • Undefined соответствует false
    • Null соответствует false
    • Boolean соответствует значению булева типа
    • Number соответствует false, если +0, -0, or NaN, в остальных случаях true
    • String соответствует false, если строка пустая '', в остальных случаях true
    if ([0] && []) {
      // true
      // Массив (даже пустой) является объектом, а объекты возвращают true
    }

  • 15.3 Используйте сокращения для булевских типов, а для строк и чисел применяйте явное сравнение.

    // плохо
    if (isValid === true) {
      // ...
    }
    
    // хорошо
    if (isValid) {
      // ...
    }
    
    // плохо
    if (name) {
      // ...
    }
    
    // хорошо
    if (name !== '') {
      // ...
    }
    
    // плохо
    if (collection.length) {
      // ...
    }
    
    // хорошо
    if (collection.length > 0) {
      // ...
    }

  • 15.5 Используйте фигурные скобки для case и default, если они содержат лексические декларации (например, let, const, function, и class). eslint: no-case-declarations.

    Почему? Лексические декларации видны во всем switch блоке, но инициализируются только при присваивании, которое происходит при входе в блок case. Возникают проблемы, когда множество case пытаются определить одно и то же.

    // плохо
    switch (foo) {
      case 1:
        let x = 1;
        break;
      case 2:
        const y = 2;
        break;
      case 3:
        function f() {
          // ...
        }
        break;
      default:
        class C {}
    }
    
    // хорошо
    switch (foo) {
      case 1: {
        let x = 1;
        break;
      }
      case 2: {
        const y = 2;
        break;
      }
      case 3: {
        function f() {
          // ...
        }
        break;
      }
      case 4:
        bar();
        break;
      default: {
        class C {}
      }
    }

  • 15.6 Тернарные операторы не должны быть вложены и в большинстве случаев должны быть расположены на одной строке. eslint: no-nested-ternary.

    // плохо
    const foo = maybe1 > maybe2
      ? "bar"
      : value1 > value2 ? "baz" : null;
    
    // лучше
    const maybeNull = value1 > value2 ? 'baz' : null;
    
    const foo = maybe1 > maybe2
      ? 'bar'
      : maybeNull;
    
    // отлично
    const maybeNull = value1 > value2 ? 'baz' : null;
    
    const foo = maybe1 > maybe2 ? 'bar' : maybeNull;

  • 15.7 Избегайте ненужных тернарных операторов. eslint: no-unneeded-ternary.

    // плохо
    const foo = a ? a : b;
    const bar = c ? true : false;
    const baz = c ? false : true;
    
    // хорошо
    const foo = a || b;
    const bar = !!c;
    const baz = !c;

⬆ к оглавлению

  • 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() возвращает новый элемент
    // соответствующий переданному названию тега
    //
    // @param {String} tag
    // @return {Element} element
    function make(tag) {
    
      // ...
    
      return element;
    }
    
    // хорошо
    /**
     * make() возвращает новый элемент
     * соответствующий переданному названию тега
     */
    function make(tag) {
    
      // ...
    
      return element;
    }

  • 17.2 Используйте двойной слэш // для однострочных комментариев. Располагайте такие комментарии отдельной строкой над кодом, который хотите пояснить. Если комментарий не является первой строкой блока, добавьте сверху пустую строку.

    // плохо
    const active = true;  // это текущая вкладка
    
    // хорошо
    // это текущая вкладка
    const active = true;
    
    // плохо
    function getType() {
      console.log('fetching type...');
      // установить по умолчанию тип 'no type'
      const type = this.type || 'no type';
    
      return type;
    }
    
    // хорошо
    function getType() {
      console.log('fetching type...');
    
      // установить по умолчанию тип 'no type'
      const type = this.type || 'no type';
    
      return type;
    }
    
    // тоже хорошо
    function getType() {
      // установить по умолчанию тип 'no type'
      const type = this.type || 'no type';
    
      return type;
    }
  • 17.3 Начинайте все комментарии с пробела, так их проще читать. eslint: spaced-comment

    // плохо
    //это текущая вкладка
    const active = true;
    
    // хорошо
    // это текущая вкладка
    const active = true;
    
    // плохо
    /**
     *make() возвращает новый элемент
     *соответствующий переданному названию тега
     */
    function make(tag) {
    
      // ...
    
      return element;
    }
    
    // хорошо
    /**
     * make() возвращает новый элемент
     * соответствующий переданному названию тега
     */
    function make(tag) {
    
      // ...
    
      return element;
    }

  • 17.4 Если комментарий начинается со слов FIXME или TODO, то это помогает другим разработчикам быстро понять, когда вы хотите указать на проблему, которую надо решить, или когда вы предлагаете решение проблемы, которое надо реализовать. Такие комментарии, в отличие от обычных, побуждают к действию: FIXME: -- нужно разобраться с этим или TODO: -- нужно реализовать.

  • 17.5 Используйте // FIXME:, чтобы описать проблему.

    class Calculator extends Abacus {
      constructor() {
        super();
    
        // FIXME: здесь не должна использоваться глобальная переменная
        total = 0;
      }
    }

  • 17.6 Используйте // TODO:, чтобы описать решение проблемы.

    class Calculator extends Abacus {
      constructor() {
        super();
    
        // TODO: нужна возможность задать total через параметры
        this.total = 0;
      }
    }

⬆ к оглавлению

  • 18.1 Используйте мягкую табуляцию (символ пробела) шириной в 2 пробела. eslint: indent jscs: validateIndentation

    // плохо
    function foo() {
    ∙∙∙∙let name;
    }
    
    // плохо
    function bar() {
    ∙let name;
    }
    
    // хорошо
    function baz() {
    ∙∙let 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 Используйте переносы строк и отступы, когда делаете длинные цепочки методов (больше 2-х методов). Ставьте точку в начале строки, чтобы дать понять, что это не новая инструкция, а продолжение цепочки. eslint: newline-per-chained-call no-whitespace-before-property

    // плохо
    $('#items').find('.selected').highlight().end().find('.open').updateCount();
    
    // плохо
    $('#items').
      find('.selected').
        highlight().
        end().
      find('.open').
        updateCount();
    
    // хорошо
    $('#items')
      .find('.selected')
        .highlight()
        .end()
      .find('.open')
        .updateCount();
    
    // плохо
    const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true)
        .attr('width', (radius + margin) * 2).append('svg:g')
        .attr('transform', `translate(${radius + margin},${radius + margin})`)
        .call(tron.led);
    
    // хорошо
    const leds = stage.selectAll('.led')
        .data(data)
      .enter().append('svg:svg')
        .classed('led', true)
        .attr('width', (radius + margin) * 2)
      .append('svg:g')
        .attr('transform', `translate(${radius + margin},${radius + margin})`)
        .call(tron.led);
    
    // хорошо
    const leds = stage.selectAll('.led').data(data);

  • 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);
    
    }
    
    // also bad
    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

    Почему? Такой подход дает понятную разницу при просмотре изменений. Кроме того, транспиляторы типа Babel удалят висячие запятые из собранного кода, поэтому вы можете не беспокоиться о проблемах в старых браузерах.

    // плохо - git diff без висячей запятой
    const hero = {
         firstName: 'Florence',
    -    lastName: 'Nightingale'
    +    lastName: 'Nightingale',
    +    inventorOf: ['coxcomb chart', 'modern nursing']
    };
    
    // хорошо - git diff с висячей запятой
    const hero = {
         firstName: 'Florence',
         lastName: 'Nightingale',
    +    inventorOf: ['coxcomb chart', 'modern nursing'],
    };
    // плохо
    const hero = {
      firstName: 'Dana',
      lastName: 'Scully'
    };
    
    const heroes = [
      'Batman',
      'Superman'
    ];
    
    // хорошо
    const hero = {
      firstName: 'Dana',
      lastName: 'Scully',
    };
    
    const heroes = [
      'Batman',
      'Superman',
    ];
    
    // плохо
    function createHero(
      firstName,
      lastName,
      inventorOf
    ) {
      // ничего не делает
    }
    
    // хорошо
    function createHero(
      firstName,
      lastName,
      inventorOf,
    ) {
      // ничего не делает
    }
    
    // хорошо (обратите внимание, что висячей запятой не должно быть после "rest"-параметра)
    function createHero(
      firstName,
      lastName,
      inventorOf,
      ...heroArgs
    ) {
      // ничего не делает
    }
    
    // плохо
    createHero(
      firstName,
      lastName,
      inventorOf
    );
    
    // хорошо
    createHero(
      firstName,
      lastName,
      inventorOf,
    );
    
    // хорошо (обратите внимание, что висячей запятой не должно быть после "rest"-аргумента)
    createHero(
      firstName,
      lastName,
      inventorOf,
      ...heroArgs
    );

⬆ к оглавлению

  • 20.1 Да. eslint: semi jscs: requireSemicolons

    // плохо
    (function () {
      const name = 'Skywalker'
      return name
    })()
    
    // хорошо
    (function () {
      const name = 'Skywalker';
      return name;
    }());
    
    // хорошо, но уже устарело
    // (такая защита функций нужна, когда конкатернируются два файла, содержащие немедленно вызываемые функции)
    ;((() => {
      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.
     * побитовый сдвиг строки для приведения ее к числу
     * работает значительно быстрее.
     */
    const val = inputValue >> 0;

  • 21.5 Примечание: Будьте осторожны с побитовыми операциями. Числа в JavaScript являются 64-битными значениями, но побитовые операции всегда возвращают 32-битные значенения (источник). Побитовый сдвиг может привести к неожиданному поведению для значений больше, чем 32 бита. Discussion. Верхний предел — 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);
    
    // отлично
    const hasAge = !!age;

⬆ к оглавлению

  • 22.1 Избегайте названий из одной буквы. Имя должно быть наглядным. eslint: id-length

    // плохо
    function q() {
      // ...
    }
    
    // хорошо
    function query() {
      // ...
    }

  • 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 Не используйте _ в начале или в конце названий. eslint: no-underscore-dangle jscs: disallowDanglingUnderscores

    Почему? JavaScript не имеет концепции приватности свойств или методов. Хотя подчеркивание в начале имени является распространенным соглашением, которое показывает “приватность”, фактически эти свойства являются такими же доступными, как и часть вашего публичного API. Это соглашение может привести к тому, что разработчики будут ошибочно думать, что изменения не приведут к поломке или что тесты не нужны. Итог: если вы хотите, чтобы что-то было “приватным”, то оно не должно быть доступно извне.

    // плохо
    this.__firstName__ = 'Panda';
    this.firstName_ = 'Panda';
    this._firstName = 'Panda';
    
    // хорошо
    this.firstName = 'Panda';

  • 22.5 Не сохраняйте ссылку на this. Используйте стрелочные функции или метод 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"
    // ^ поддерживает оба варианта: insideDirectory.js и insideDirectory/index.js

  • 22.7 Используйте camelCase, когда экспортируете функцию по умолчанию. Ваш файл должен называться так же, как и имя функции.
    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 Не используйте геттеры/сеттеры, т.к. они вызывают неожиданные побочные эффекты, а также их тяжело тестировать, поддерживать и понимать. Вместо этого создавайте методы getVal() и setVal('hello').

    // плохо
    class Dragon {
      get age() {
        // ...
      }
    
      set age(value) {
        // ...
      }
    }
    
    // хорошо
    class Dragon {
      getAge() {
        // ...
      }
    
      setAge(value) {
        // ...
      }
    }

  • 23.3 Если свойство/метод возвращает логический тип, то используйте названия 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();

⬆ к оглавлению

⬆ к оглавлению

  • 27.1 Здесь собраны ссылки на различные возможности ES6.
  1. Стрелочные функции
  2. Классы и конструкторы
  3. Сокращенная запись методов объекта
  4. Сокращенная запись свойств объекта
  5. Вычисляемые имена свойств объекта
  6. Шаблонные строки
  7. Деструктуризация
  8. Параметры по умолчанию
  9. Оставшиеся параметры
  10. Оператор расширения
  11. Let и Const
  12. Итераторы и генераторы
  13. Модули

  • 27.2 Не используйте предложения TC39, которые не перешли на 3 стадию.

    Почему? Они ещё не закончены и могут быть изменены или полностью изъяты. Мы хотим использовать JavaScript, а предложения еще не стали частью JavaScript.

⬆ к оглавлению

  • 28.1 Ага.

    function foo() {
      return true;
    }

  • 28.2 Нет, но серьезно:
  • Какой бы фреймворк вы не использовали, вы должны писать тесты!
  • Стремитесь к тому, чтобы написать много маленьких чистых функций, и к тому, чтобы свести к минимуму места, где происходят мутации.
  • Будьте осторожны со стабами (stubs) и моками (mocks) — они могут сделать ваше тестирование хрупким.
  • Мы в первую очередь советуем вам использовать mocha от Airbnb. tape также иногда используется для небольших, отдельных модулей.
  • 100% покрытие тестами — это хорошая цель, к которой надо стремиться, даже если это не всегда практично.
  • Всякий раз, когда вы исправляете ошибку, пишите регрессионный тест. Исправленная ошибка без регрессионного тестирования почти наверняка всплывет в будущем.

⬆ к оглавлению

⬆ к оглавлению

Изучение ES6

Почитайте это

Инструменты

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

Другие стили

Дальнейшее чтение

Книги

Блоги

Подкасты

⬆ к оглавлению

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

⬆ к оглавлению

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

⬆ к оглавлению

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

(The MIT License)

Copyright (c) 2014-2017 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.

⬆ к оглавлению

};

About

Перевод «JavaScript Style Guide» от Airbnb

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published