Стиль написания кода на JavaScript для Uprock: Перевод и расширение Airbnb JavaScript Style Guide

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

  1. Типы
  2. Объекты
  3. Массивы
  4. Строки
  5. Функции
  6. Свойства
  7. Переменные
  8. Области видимости
  9. Условные выражения и равенства
  10. Блоки кода
  11. Комментарии
  12. Пробелы
  13. Запятые
  14. Точки с запятой
  15. Приведение типов
  16. Соглашение об именовании
  17. Геттеры и сеттеры
  18. Конструкторы
  19. События
  20. Модули
  21. jQuery
  22. Совместимость с ES5
  23. Тестирование
  24. Быстродействие
  25. Ресурсы
  26. В реальном мире
  27. Переводы
  28. Лицензия
  • Простые типы: Когда вы взаимодействуете с простым типом, вы взаимодействуете непосредственно с его значением в памяти.

    • string
    • number
    • boolean
    • null
    • undefined
    var foo = 1,
        bar = foo;
    
    bar = 9;
    
    console.log(foo, bar); // => 1, 9. foo не изменился
  • Сложные типы: Когда вы взаимодействуете со сложным типом, вы взаимодействуете с ссылкой на его значение в памяти.

    • object
    • array
    • function
    var foo = [1, 2],
        bar = foo;
    
    bar[0] = 9;
    
    console.log(foo[0], bar[0]); // => 9, 9.

    [⬆]

  • Для создания объекта используйте фигурные скобки. Не создавайте объекты через конструктор new Object.

    // плохо
    var item = new Object();
    
    // хорошо
    var item = {};
  • Не используйте зарезервированные слова в качестве ключей объектов. Они не будут работать в IE8. Подробнее

    // плохо
    var superman = {
      default: { clark: 'kent' },
      private: true
    };
    
    // хорошо
    var superman = {
      defaults: { clark: 'kent' },
      hidden: true
    };
  • Не используйте ключевые слова (в том числе измененные). Вместо них используйте синонимы.

    // плохо
    var superman = {
      class: 'alien'
    };
    
    // плохо
    var superman = {
      klass: 'alien'
    };
    
    // хорошо
    var superman = {
      type: 'alien'
    };

    [⬆]

  • Для создания массива используйте квадратные скобки. Не создавайте массивы через конструктор new Array.

    // плохо
    var items = new Array();
    
    // хорошо
    var items = [];
  • Если вы не знаете длину массива, используйте Array::push.

    var someStack = [];
    
    
    // плохо
    someStack[someStack.length] = 'abracadabra';
    
    // хорошо
    someStack.push('abracadabra');
  • Если вам необходимо скопировать массив, используйте Array::slice. jsPerf

    var len = items.length,
        itemsCopy = [],
        i;
    
    // плохо
    for (i = 0; i < len; i++) {
      itemsCopy[i] = items[i];
    }
    
    // хорошо
    itemsCopy = items.slice();
  • Чтобы скопировать похожий по свойствам на массив объект (например, NodeList или Arguments), используйте Array::slice.

    function trigger() {
      var args = Array.prototype.slice.call(arguments);
      ...
    }

    [⬆]

  • Используйте одинарные кавычки '' для строк.

    // плохо
    var name = "Боб Дилан";
    
    // хорошо
    var name = 'Боб Дилан';
    
    // плохо
    var fullName = "Боб " + this.lastName;
    
    // хорошо
    var fullName = 'Дилан ' + this.lastName;
  • Строки длиннее 80 символов нужно разделять, выполняя перенос через конкатенацию строк.

  • Осторожно: строки с большим количеством конкатенаций могут отрицательно влиять на быстродействие. jsPerf и Обсуждение

    // плохо
    var errorMessage = 'Эта сверхдлинная ошибка возникла из-за белой обезъяны. Не говори про обезъяну! Не слушай об обезьяне! Не думай об обезъяне!';
    
    // плохо
    var errorMessage = 'Эта сверхдлинная ошибка возникла из-за белой обезъяны. \
    Не говори про обезъяну! Не слушай об обезьяне! \
    Не думай об обезъяне!';
    
    // хорошо
    var errorMessage = 'Эта сверхдлинная ошибка возникла из-за белой обезъяны. ' +
      'Не говори про обезъяну! Не слушай об обезьяне! ' +
      'Не думай об обезъяне!';
  • Когда строка создается программным путем, используйте Array::join вместо объединения строк. В основном для IE: jsPerf.

    var items,
        messages,
        length,
        i;
    
    messages = [{
        state: 'success',
        message: 'Это работает.'
    },{
        state: 'success',
        message: 'Это тоже.'
    },{
        state: 'error',
        message: 'А я томат.'
    }];
    
    length = messages.length;
    
    // плохо
    function inbox(messages) {
      items = '<ul>';
    
      for (i = 0; i < length; i++) {
        items += '<li>' + messages[i].message + '</li>';
      }
    
      return items + '</ul>';
    }
    
    // хорошо
    function inbox(messages) {
      items = [];
    
      for (i = 0; i < length; i++) {
        items[i] = messages[i].message;
      }
    
      return '<ul><li>' + items.join('</li><li>') + '</li></ul>';
    }

    [⬆]

  • Объявление функций:

    // объявление анонимной функции
    var anonymous = function() {
      return true;
    };
    
    // объявление именованной функции
    var named = function named() {
      return true;
    };
    
    // объявление функции, которая сразу же выполняется (замыкание)
    (function() {
      console.log('Если вы читаете это, вы открыли консоль.');
    })();
  • Никогда не объявляйте функцию внутри блока кода — например в if, while, else и так далее. Единственное исключение — блок функции. Вместо этого присваивайте функцию уже объявленной через var переменной. Условное объявление функций работает, но в различных браузерах работает по-разному.

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

    // плохо
    if (currentUser) {
      function test() {
        console.log('Плохой мальчик.');
      }
    }
    
    // хорошо
    var test;
    if (currentUser) {
      test = function test() {
        console.log('Молодец.');
      };
    }
  • Никогда не используйте аргумент функции arguments, он будет более приоритетным над объектом arguments, который доступен без объявления для каждой функции.

    // плохо
    function nope(name, options, arguments) {
      // ...код...
    }
    
    // хорошо
    function yup(name, options, args) {
      // ...код...
    }

    [⬆]

  • Используйте точечную нотацию для доступа к свойствам и методам.

    var luke = {
      jedi: true,
      age: 28
    };
    
    // плохо
    var isJedi = luke['jedi'];
    
    // хорошо
    var isJedi = luke.jedi;
  • Используйте нотацию с [], когда вы получаете свойство, имя для которого хранится в переменной.

    var luke = {
      jedi: true,
      age: 28
    };
    
    function getProp(prop) {
      return luke[prop];
    }
    
    var isJedi = getProp('jedi');

    [⬆]

  • Всегда используйте var для объявления переменных. В противном случае переменная будет объявлена глобальной. Загрязнение глобального пространства имен — всегда плохо.

    // плохо
    superPower = new SuperPower();
    
    // хорошо
    var superPower = new SuperPower();
  • Используйте одно var объявление переменных для всех переменных, и объявляйте каждую переменную на новой строке.

    // плохо
    var items = getItems();
    var goSportsTeam = true;
    var dragonball = 'z';
    
    // хорошо
    var items = getItems(),
        goSportsTeam = true,
        dragonball = 'z';
  • Объявляйте переменные, которым не присваивается значение, в конце. Это удобно, когда вам необходимо задать значение одной из этих переменных на базе уже присвоенных значений.

    // плохо
    var i, len, dragonball,
        items = getItems(),
        goSportsTeam = true;
    
    // плохо
    var i, items = getItems(),
        dragonball,
        goSportsTeam = true,
        len;
    
    // хорошо
    var items = getItems(),
        goSportsTeam = true,
        dragonball,
        length,
        i;
  • Присваивайте переменные в начале области видимости. Это помогает избегать проблем с объявлением переменных и областями видимости.

    // плохо
    function() {
      test();
      console.log('делаю что-нибудь..');
    
      //..или не делаю...
    
      var name = getName();
    
      if (name === 'test') {
        return false;
      }
    
      return name;
    }
    
    // хорошо
    function() {
      var name = getName();
    
      test();
      console.log('делаю что-то полезное..');
    
      //..продолжаю приносить пользу людям..
    
      if (name === 'test') {
        return false;
      }
    
      return name;
    }
    
    // плохо
    function() {
      var name = getName();
    
      if (!arguments.length) {
        return false;
      }
    
      return true;
    }
    
    // хорошо
    function() {
      if (!arguments.length) {
        return false;
      }
    
      var name = getName();
    
      return true;
    }

    [⬆]

  • Объявление переменных ограничивается областью видимости, а присвоение — нет.

    // Мы знаем, что это не будет работать
    // если нет глобальной переменной notDefined
    function example() {
      console.log(notDefined); // => выбрасывает код с ошибкой ReferenceError
    }
    
    // Декларирование переменной после ссылки на нее
    // не будет работать из-за ограничения области видимости.
    function example() {
      console.log(declaredButNotAssigned); // => undefined
      var declaredButNotAssigned = true;
    }
    
    // Интерпретатор переносит объявление переменной
    // к верху области видимости.
    // Что значит, что предыдущий пример в действительности
    // будет воспринят интерпретатором так:
    function example() {
      var declaredButNotAssigned;
      console.log(declaredButNotAssigned); // => undefined
      declaredButNotAssigned = true;
    }
  • Объявление анонимной функции поднимает наверх области видимости саму переменную, но не ее значение.

    function example() {
      console.log(anonymous); // => undefined
    
      anonymous(); // => TypeError anonymous is not a function
      // Ошибка типов: переменная anonymous не является функцией и не может быть вызвана
    
      var anonymous = function() {
        console.log('анонимная функция');
      };
    }
  • Именованные функции поднимают наверх области видимости переменную, не ее значение. Имя функции при этом недоступно в области видимости переменной и доступно только изнутри.

    function example() {
      console.log(named); // => undefined
    
      named(); // => TypeError named is not a function
      // Ошибка типов: переменная named не является функцией и не может быть вызвана
    
      superPower(); // => ReferenceError superPower is not defined (Ошибка ссылки: переменная superPower не найдена в этой области видимости)
    
      var named = function superPower() {
        console.log('Я лечууууу');
      };
    }
    
    // То же самое происходит, когда имя функции и имя переменной совпадают.
    // var named доступно изнутри области видимости функции example.
    // function named доступна только изнутри ее самой.
    function example() {
      console.log(named); // => undefined
    
      named(); // => TypeError named is not a function
      // Ошибка типов: переменная named не является функцией и не может быть вызвана
    
      var named = function named() {
        console.log('именованная функция');
      }
    }
  • Объявления функции поднимают на верх текущей области видимости и имя, и свое значение.

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

    [⬆]

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

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

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

    // плохо
    if (name !== '') {
      // ...код...
    }
    
    // хорошо
    if (name) {
      // ...код...
    }
    
    // плохо
    if (collection.length > 0) {
      // ...код...
    }
    
    // хорошо
    if (collection.length) {
      // ...код...
    }
  • Более подробно можно прочитать в статье Truth Equality and JavaScript от Angus Croll

    [⬆]

  • Используйте фигурные скобки для всех многострочных блоков.

    // плохо
    if (test)
      return false;
    
    // хорошо
    if (test) return false;
    
    // хорошо
    if (test) {
      return false;
    }
    
    // плохо
    function() { return false; }
    
    // хорошо
    function() {
      return false;
    }

    [⬆]

  • Используйте /** ... */ для многострочных комментариев. Включите описание, опишите типы и значения для всех параметров и возвращаемых значений в формате jsdoc.

    // плохо
    // make() возвращает новый элемент
    // основываясь на получаемом имени тэга
    //
    // @param <String> tag
    // @return <Element> element
    function make(tag) {
    
      // ...создаем element...
    
      return element;
    }
    
    // хорошо
    /**
     * make() возвращает новый элемент
     * основываясь на получаемом имени тэга
     *
     * @param <String> tag
     * @return <Element> element
     */
    function make(tag) {
    
      // ...создаем element...
    
      return element;
    }
  • Используйте // для комментариев в одну строку. Размещайте комментарии на новой строке над темой комментария. Добавляйте пустую строку над комментарием.

    // плохо
    var active = true;  // устанавливаем активным элементом
    
    // хорошо
    // устанавливаем активным элементом
    var active = true;
    
    // плохо
    function getType() {
      console.log('проверяем тип...');
      // задаем тип по умолчанию 'no type'
      var type = this._type || 'no type';
    
      return type;
    }
    
    // хорошо
    function getType() {
      console.log('проверяем тип...');
    
      // задаем тип по умолчанию 'no type'
      var type = this._type || 'no type';
    
      return type;
    }
  • Префикс TODO помогает другим разработчикам быстро понять, что вы указываете на проблему, к которой нужно вернуться в дальнейшем, или если вы предлагете решение проблемы, которое должно быть реализовано. Эти комментарии отличаются от обычных комментариев, так как не описывают текущее поведение, а призывают к действию, например TODO -- нужно реализовать интерфейс. Такие комментарии также автоматически обнаруживаются многими IDE и редакторами кода, что позволяет быстро перемещаться между ними.

  • Используйте // TODO FIXME: для аннотирования проблем

    function Calculator() {
    
      // TODO FIXME: тут не нужно использовать глобальную переменную
      total = 0;
    
      return this;
    }
  • Используйте // TODO: для указания решений проблем

    function Calculator() {
    
      // TODO: должна быть возможность изменять значение через параметр функции
      this.total = 0;
    
      return this;
    }

  **[[⬆]](#Оглавление)**


## <a name='whitespace'>Пробелы</a>

- Используйте программную табуляцию (ее поддерживают все современные редакторы кода и IDE) из двух пробелов.

  ```javascript
  // плохо
  function() {
  ∙∙∙∙var name;
  }

  // плохо
  function() {
  ∙var name;
  }

  // хорошо
  function() {
  ∙∙var name;
  }
  ```
- Устанавливайте один пробел перед открывающей скобкой.

  ```javascript
  // плохо
  function test(){
    console.log('test');
  }

  // хорошо
  function test() {
    console.log('test');
  }

  // плохо
  dog.set('attr',{
    age: '1 year',
    breed: 'Bernese Mountain Dog'
  });

  // хорошо
  dog.set('attr', {
    age: '1 year',
    breed: 'Bernese Mountain Dog'
  });
  ```
- Оставляйте новую строку в конце файла.

  ```javascript
  // плохо
  (function(global) {
    // ...код...
  })(this);
  ```

  ```javascript
  // хорошо
  (function(global) {
    // ...код...
  })(this);

  ```

- Используйте отступы, когда делаете цепочки вызовов.

  ```javascript
  // плохо
  $('#items').find('.selected').highlight().end().find('.open').updateCount();

  // хорошо
  $('#items')
    .find('.selected')
      .highlight()
      .end()
    .find('.open')
      .updateCount();

  // плохо
  var leds = stage.selectAll('.led').data(data).enter().append('svg:svg').class('led', true)
      .attr('width',  (radius + margin) * 2).append('svg:g')
      .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
      .call(tron.led);

  // хорошо
  var leds = stage.selectAll('.led')
      .data(data)
    .enter().append('svg:svg')
      .class('led', true)
      .attr('width',  (radius + margin) * 2)
    .append('svg:g')
      .attr('transform', 'translate(' + (radius + margin) + ',' + (radius + margin) + ')')
      .call(tron.led);
  ```

  **[[⬆]](#Оглавление)**

## <a name='commas'>Запятые</a>

- Запятые в начале строки: **Нет.**

  ```javascript
  // плохо
  var once
    , upon
    , aTime;

  // хорошо
  var once,
      upon,
      aTime;

  // плохо
  var hero = {
      firstName: 'Bob'
    , lastName: 'Parr'
    , heroName: 'Mr. Incredible'
    , superPower: 'strength'
  };

  // хорошо
  var hero = {
    firstName: 'Bob',
    lastName: 'Parr',
    heroName: 'Mr. Incredible',
    superPower: 'strength'
  };
  ```

- Дополнительная запятая в конце объектов: **Нет**. Она способна вызвать проблемы с IE6/7 и IE9 в режиме совместимости. В некоторых реализациях ES3 запятая в конце массива увеличивает его длину на 1, что может вызвать проблемы. Этот вопрос был прояснен только в ES5 ([оригинал](http://es5.github.io/#D)):

> Редакция ECMAScript 5 однозначно устанавливает факт, что запятая в конце ArrayInitialiser не должна увеличивать длину массива. Это несемантическое изменение от Редакции ECMAScript 3, но некоторые реализации до этого некорректно разрешали этот вопрос.

  ```javascript
  // плохо
  var hero = {
    firstName: 'Kevin',
    lastName: 'Flynn',
  };

  var heroes = [
    'Batman',
    'Superman',
  ];

  // хорошо
  var hero = {
    firstName: 'Kevin',
    lastName: 'Flynn'
  };

  var heroes = [
    'Batman',
    'Superman'
  ];
  ```

  **[[⬆]](#Оглавление)**


## <a name='semicolons'>Точки с запятой</a>

- **Да.**

  ```javascript
  // плохо
  (function() {
    var name = 'Skywalker'
    return name
  })()

  // хорошо
  (function() {
    var name = 'Skywalker';
    return name;
  })();

  // хорошо
  ;(function() {
    var name = 'Skywalker';
    return name;
  })();
  ```

  **[[⬆]](#Оглавление)**


## <a name='type-coercion'>Приведение типов</a>

- Выполняйте приведение типов в начале операции, но не делайте его избыточным.
- Строки:

  ```javascript
  //  => this.reviewScore = 9;

  // плохо
  var totalScore = this.reviewScore + '';

  // хорошо
  var totalScore = '' + this.reviewScore;

  // плохо
  var totalScore = '' + this.reviewScore + ' итого';

  // хорошо
  var totalScore = this.reviewScore + ' итого';
  ```

- Используйте `parseInt` для чисел и всегда указывайте основание для приведения типов.

  ```javascript
  var inputValue = '4';

  // плохо
  var val = new Number(inputValue);

  // плохо
  var val = +inputValue;

  // плохо
  var val = inputValue >> 0;

  // плохо
  var val = parseInt(inputValue);

  // хорошо
  var val = Number(inputValue);

  // хорошо
  var val = parseInt(inputValue, 10);
  ```

- Если по какой-либо причине вы делаете что-то дикое, и именно на `parseInt` тратится больше всего ресурсов, используйте побитовый сдвиг [из соображений быстродействия](http://jsperf.com/coercion-vs-casting/3), но обязательно оставьте комментарий с объяснением причин.

  ```javascript
  // хорошо
  /**
   * этот код медленно работал из-за parseInt
   * побитовый сдвиг строки для приведения ее к числу
   * работает значительно быстрее.
   */
  var val = inputValue >> 0;
  ```

- **Примечание:** Будьте осторожны с побитовыми операциями. Числа в JavaScript являются [64-битными значениями](http://es5.github.io/#x4.3.19), но побитовые операции всегда возвращают 32-битные значенения. [Источник](http://es5.github.io/#x11.7). Побитовые операции над числами, значение которых выходит за 32 бита (верхний предел: 2,147,483,647).

  ```
  2147483647 >> 0 //=> 2147483647
  2147483648 >> 0 //=> -2147483648
  2147483649 >> 0 //=> -2147483647
  ```

- логические типы(Boolean):

  ```javascript
  var age = 0;

  // плохо
  var hasAge = new Boolean(age);

  // хорошо
  var hasAge = Boolean(age);

  // хорошо
  var hasAge = !!age;
  ```

  **[[⬆]](#Оглавление)**


## <a name='naming-conventions'>Соглашение об именовании</a>

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

  ```javascript
  // плохо
  function q() {
    // ...код...
  }

  // хорошо
  function query() {
    // ...код...
  }
  ```

- Используйте camelCase для именования объектов, функций и переменных.

  ```javascript
  // плохо
  var OBJEcttsssss = {};
  var this_is_my_object = {};
  function c() {};
  var u = new user({
    name: 'Bob Parr'
  });

  // хорошо
  var thisIsMyObject = {};
  function thisIsMyFunction() {};
  var user = new User({
    name: 'Bob Parr'
  });
  ```

- Используйте PascalCase для именования конструкторов классов

  ```javascript
  // плохо
  function user(options) {
    this.name = options.name;
  }

  var bad = new user({
    name: 'Плохиш'
  });

  // хорошо
  function User(options) {
    this.name = options.name;
  }

  var good = new User({
    name: 'Кибальчиш'
  });
  ```

- Используйте подчеркивание `_` в качестве префикса для именования внутренних методов и переменных объекта.

  ```javascript
  // плохо
  this.__firstName__ = 'Panda';
  this.firstName_ = 'Panda';

  // хорошо
  this._firstName = 'Panda';
  ```

- Создавая ссылку на `this`, используйте `_this`.

  ```javascript
  // плохо
  function() {
    var self = this;
    return function() {
      console.log(self);
    };
  }

  // плохо
  function() {
    var that = this;
    return function() {
      console.log(that);
    };
  }

  // хорошо
  function() {
    var _this = this;
    return function() {
      console.log(_this);
    };
  }
  ```

- Задавайте имена для функций. Это повышает читаемость сообщений об ошибках кода.

  ```javascript
  // плохо
  var log = function(msg) {
    console.log(msg);
  };

  // хорошо
  var log = function log(msg) {
    console.log(msg);
  };
  ```

  **[[⬆]](#Оглавление)**


## <a name='accessors'>Геттеры и сеттеры: функции для доступа к значениям объекта</a>

- Функции универсального доступа к свойствам не требуются
- Если вам необходимо создать функцию для доступа к переменной, используйте раздельные функции getVal() и setVal('hello')

  ```javascript
  // плохо
  dragon.age();

  // хорошо
  dragon.getAge();

  // плохо
  dragon.age(25);

  // хорошо
  dragon.setAge(25);
  ```

- Если свойство является логическим(boolean), используйте isVal() или hasVal()

  ```javascript
  // плохо
  if (!dragon.age()) {
    return false;
  }

  // хорошо
  if (!dragon.hasAge()) {
    return false;
  }
  ```

- Вы можете создавать функции get() и set(), но будьте логичны и последовательны – то есть не добавляйте свойства, которые не могут быть изменены через эти функции.

  ```javascript
  function Jedi(options) {
    options || (options = {});
    var lightsaber = options.lightsaber || 'blue';
    this.set('lightsaber', lightsaber);
  }

  Jedi.prototype.set = function(key, val) {
    this[key] = val;
  };

  Jedi.prototype.get = function(key) {
    return this[key];
  };
  ```

  **[[⬆]](#Оглавление)**


## <a name='constructors'>Конструкторы</a>

- Присваивайте метод прототипу вместо замены прототипа на другой объект. Замена прототипа на другой объект делает наследование невозможным.

  ```javascript
  function Jedi() {
    console.log('new jedi');
  }

  // плохо
  Jedi.prototype = {
    fight: function fight() {
      console.log('fighting');
    },

    block: function block() {
      console.log('blocking');
    }
  };

  // хорошо
  Jedi.prototype.fight = function fight() {
    console.log('fighting');
  };

  Jedi.prototype.block = function block() {
    console.log('blocking');
  };
  ```

- Методы могут возвращать `this` для создания цепочек вызовов. Но стоит оставаться последовательным и обеспечить одинаковое поведение для всех методов, кроме геттеров.

  ```javascript
  // плохо
  Jedi.prototype.jump = function() {
    this.jumping = true;
    return true;
  };

  Jedi.prototype.setHeight = function(height) {
    this.height = height;
  };

  var luke = new Jedi();
  luke.jump(); // => true
  luke.setHeight(20) // => undefined

  // хорошо
  Jedi.prototype.jump = function() {
    this.jumping = true;
    return this;
  };

  Jedi.prototype.setHeight = function(height) {
    this.height = height;
    return this;
  };

  var luke = new Jedi();

  luke.jump()
    .setHeight(20);
  ```


- Вы можете заменить стандартный метод toString(), но убедитесь, что он работает и не вызывает побочных эффектов.

  ```javascript
  function Jedi(options) {
    options || (options = {});
    this.name = options.name || 'no name';
  }

  Jedi.prototype.getName = function getName() {
    return this.name;
  };

  Jedi.prototype.toString = function toString() {
    return 'Jedi - ' + this.getName();
  };
  ```

  **[[⬆]](#Оглавление)**


## <a name='events'>События</a>

- Подключая набор данных к событиям (как DOM-событиям, так и js-событиям, например, в Backbone), передавайте объект вместо простой переменной. Это позволяет в процессе всплытия событий добавлять к данному объекту дополнительную информацию.

  ```js
  // плохо
  $(this).trigger('listingUpdated', listing.id);

  ...

  $(this).on('listingUpdated', function(e, listing) {
       //делаем что-нибудь с listing, например:
       listing.name = listings[listing.id]
  });
  ```

  prefer:

  ```js
  // хорошо
  $(this).trigger('listingUpdated', { listingId : listing.id });

  ...

  $(this).on('listingUpdated', function(e, data) {
    // делаем что-нибудь с data.listingId
  });
  ```

**[[⬆]](#Оглавление)**


## <a name='modules'>Модули</a>

- Модуль должен начинаться с `!`. За счет этого даже некорректно сформированный модуль, в конце которого отсутствует точка с запятой, не вызовет ошибок при автоматической сборке скриптов. [Объяснение](https://github.com/airbnb/javascript/issues/44#issuecomment-13063933)
- Файл должен быть именован с camelCase, находиться в папке с тем же именем, и совпадать с именем экспортируемой переменной.
- Добавьте метод noConflict(), устанавливающий экспортируемый модуль в состояние предыдущей версии.
- Всегда объявляйте `'use strict';` в начале модуля.

  ```javascript
  // fancyInput/fancyInput.js

  !function(global) {
    'use strict';

    var previousFancyInput = global.FancyInput;

    function FancyInput(options) {
      this.options = options || {};
    }

    FancyInput.noConflict = function noConflict() {
      global.FancyInput = previousFancyInput;
      return FancyInput;
    };

    global.FancyInput = FancyInput;
  }(this);
  ```

  **[[⬆]](#Оглавление)**


## <a name='jquery'>jQuery</a>

- Для jQuery-переменных используйте префикс `$`.

  ```javascript
  // плохо
  var sidebar = $('.sidebar');

  // хорошо
  var $sidebar = $('.sidebar');
  ```

- Кэшируйте jQuery-запросы. Каждый новый jQuery-запрос делает повторный поиск по DOM-дереву, и приложение начинает работать медленнее.

  ```javascript
  // плохо
  function setSidebar() {
    $('.sidebar').hide();

    // ...код...

    $('.sidebar').css({
      'background-color': 'pink'
    });
  }

  // хорошо
  function setSidebar() {
    var $sidebar = $('.sidebar');
    $sidebar.hide();

    // ...код...

    $sidebar.css({
      'background-color': 'pink'
    });
  }
  ```

- Для DOM-запросов используйте классический каскадный CSS-синтаксис `$('.sidebar ul')` или родитель > потомок `$('.sidebar > ul')`. [jsPerf](http://jsperf.com/jquery-find-vs-context-sel/16)
- Используйте `find` для поиска внутри DOM-объекта.

  ```javascript
  // плохо
  $('ul', '.sidebar').hide();

  // плохо
  $('.sidebar').find('ul').hide();

  // хорошо
  $('.sidebar ul').hide();

  // хорошо
  $('.sidebar > ul').hide();

  // хорошо
  $sidebar.find('ul');
  ```

  **[[⬆]](#Оглавление)**


## <a name='es5'>Совместимость ECMAScript 5</a>

- Опирайтесь на [таблицу совместимости](http://kangax.github.com/es5-compat-table/) с ES5 от [Kangax](https://twitter.com/kangax/)

**[[⬆]](#Оглавление)**


## <a name='testing'>Тестирование</a>

- **Да.**

  ```javascript
  function() {
    return true;
  }
  ```

  **[[⬆]](#Оглавление)**


## <a name='performance'>Быстродействие</a>

- [On Layout & Web Performance](http://kellegous.com/j/2013/01/26/layout-performance/)
- [String vs Array Concat](http://jsperf.com/string-vs-array-concat/2)
- [Try/Catch Cost In a Loop](http://jsperf.com/try-catch-in-loop-cost)
- [Bang Function](http://jsperf.com/bang-function)
- [jQuery Find vs Context, Selector](http://jsperf.com/jquery-find-vs-context-sel/13)
- [innerHTML vs textContent for script text](http://jsperf.com/innerhtml-vs-textcontent-for-script-text)
- [Long String Concatenation](http://jsperf.com/ya-string-concat)
- В процессе наполнения...

**[[⬆]](#Оглавление)**


## <a name='resources'>Ресурсы</a>


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

- [Annotated ECMAScript 5.1](http://es5.github.com/)

**Другие руководства по стилю**

- [Google JavaScript Style Guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml)
- [jQuery Core Style Guidelines](http://docs.jquery.com/JQuery_Core_Style_Guidelines)
- [Principles of Writing Consistent, Idiomatic JavaScript](https://github.com/rwldrn/idiomatic.js/)

**Другие стили**

- [Naming this in nested functions](https://gist.github.com/4135065) - Christian Johansen
- [Conditional Callbacks](https://github.com/airbnb/javascript/issues/52)
- [Popular JavaScript Coding Conventions on Github](http://sideeffect.kr/popularconvention/#javascript)

**Дальнейшее прочтение**

- [Understanding JavaScript Closures](http://javascriptweblog.wordpress.com/2010/10/25/understanding-javascript-closures/) - Angus Croll
- [Basic JavaScript for the impatient programmer](http://www.2ality.com/2013/06/basic-javascript.html) - Dr. Axel Rauschmayer

**Книги**

- [JavaScript: The Good Parts](http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742) - Douglas Crockford
- [JavaScript Patterns](http://www.amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp/0596806752) - Stoyan Stefanov
- [Pro JavaScript Design Patterns](http://www.amazon.com/JavaScript-Design-Patterns-Recipes-Problem-Solution/dp/159059908X)  - Ross Harmes and Dustin Diaz
- [High Performance Web Sites: Essential Knowledge for Front-End Engineers](http://www.amazon.com/High-Performance-Web-Sites-Essential/dp/0596529309) - Steve Souders
- [Maintainable JavaScript](http://www.amazon.com/Maintainable-JavaScript-Nicholas-C-Zakas/dp/1449327680) - Nicholas C. Zakas
- [JavaScript Web Applications](http://www.amazon.com/JavaScript-Web-Applications-Alex-MacCaw/dp/144930351X) - Alex MacCaw
- [Pro JavaScript Techniques](http://www.amazon.com/Pro-JavaScript-Techniques-John-Resig/dp/1590597273) - John Resig
- [Smashing Node.js: JavaScript Everywhere](http://www.amazon.com/Smashing-Node-js-JavaScript-Everywhere-Magazine/dp/1119962595) - Guillermo Rauch
- [Secrets of the JavaScript Ninja](http://www.amazon.com/Secrets-JavaScript-Ninja-John-Resig/dp/193398869X) - John Resig and Bear Bibeault
- [Human JavaScript](http://humanjavascript.com/) - Henrik Joreteg
- [Superhero.js](http://superherojs.com/) - Kim Joar Bekkelund, Mads Mobæk, & Olav Bjorkoy
- [JSBooks](http://jsbooks.revolunet.com/)

**Блоги**

- [DailyJS](http://dailyjs.com/)
- [JavaScript Weekly](http://javascriptweekly.com/)
- [JavaScript, JavaScript...](http://javascriptweblog.wordpress.com/)
- [Bocoup Weblog](http://weblog.bocoup.com/)
- [Adequately Good](http://www.adequatelygood.com/)
- [NCZOnline](http://www.nczonline.net/)
- [Perfection Kills](http://perfectionkills.com/)
- [Ben Alman](http://benalman.com/)
- [Dmitry Baranovskiy](http://dmitry.baranovskiy.com/)
- [Dustin Diaz](http://dustindiaz.com/)
- [nettuts](http://net.tutsplus.com/?s=javascript)

**[[⬆]](#Оглавление)**

## <a name='in-the-wild'>В реальном мире</a>

Вот неполный список организаций, которые опираются на оригинальное руководство от AirBnB. Если вы собираетесь использовать это переведенное руководство, сделайте pull request, и мы сможем начать отдельный список компаний, использующих данный перевод.

- **Aan Zee**: [AanZee/javascript](https://github.com/AanZee/javascript)
- **Airbnb**: [airbnb/javascript](https://github.com/airbnb/javascript)
- **American Insitutes for Research**: [AIRAST/javascript](https://github.com/AIRAST/javascript)
- **Compass Learning**: [compasslearning/javascript-style-guide](https://github.com/compasslearning/javascript-style-guide)
- **ExactTarget**: [ExactTarget/javascript](https://github.com/ExactTarget/javascript)
- **Gawker Media**: [gawkermedia/javascript](https://github.com/gawkermedia/javascript)
- **GeneralElectric**: [GeneralElectric/javascript](https://github.com/GeneralElectric/javascript)
- **GoodData**: [gooddata/gdc-js-style](https://github.com/gooddata/gdc-js-style)
- **Grooveshark**: [grooveshark/javascript](https://github.com/grooveshark/javascript)
- **How About We**: [howaboutwe/javascript](https://github.com/howaboutwe/javascript)
- **Mighty Spring**: [mightyspring/javascript](https://github.com/mightyspring/javascript)
- **MinnPost**: [MinnPost/javascript](https://github.com/MinnPost/javascript)
- **ModCloth**: [modcloth/javascript](https://github.com/modcloth/javascript)
- **National Geographic**: [natgeo/javascript](https://github.com/natgeo/javascript)
- **National Park Service**: [nationalparkservice/javascript](https://github.com/nationalparkservice/javascript)
- **Razorfish**: [razorfish/javascript-style-guide](https://github.com/razorfish/javascript-style-guide)
- **Shutterfly**: [shutterfly/javascript](https://github.com/shutterfly/javascript)
- **Userify**: [userify/javascript](https://github.com/userify/javascript)
- **Zillow**: [zillow/javascript](https://github.com/zillow/javascript)
- **ZocDoc**: [ZocDoc/javascript](https://github.com/ZocDoc/javascript)

## <a name='translation'>Переводы</a>

Это руководство так же переведено на:

- :de: **Немецкий**: [timofurrer/javascript-style-guide](https://github.com/timofurrer/javascript-style-guide)
- :jp: **Японский**: [mitsuruog/javacript-style-guide](https://github.com/mitsuruog/javacript-style-guide)
- :br: **Португальский**: [armoucar/javascript-style-guide](https://github.com/armoucar/javascript-style-guide)
- :cn: **Китайский**: [adamlu/javascript-style-guide](https://github.com/adamlu/javascript-style-guide)
- :es: **Испанский**: [paolocarrasco/javascript-style-guide](https://github.com/paolocarrasco/javascript-style-guide)
- :kr: **Корейский**: [tipjs/javascript-style-guide](https://github.com/tipjs/javascript-style-guide)

## <a name='license'>Лицензия</a>

(The MIT License)

Copyright (c) 2012 Airbnb: оригинальный текст, (c) 2013 Uprock: перевод

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.

**[[⬆]](#Оглавление)**