Skip to content

Latest commit

 

History

History
1564 lines (1162 loc) · 46.7 KB

js.md

File metadata and controls

1564 lines (1162 loc) · 46.7 KB

Стиль написания кода на JavaScript

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

Основано на airbnb/javascript.

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

    • 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] = 'csssr';

    // хорошо someStack.push('csssr');

    
    
  • Если вам необходимо скопировать массив, используйте 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](http://jsperf.com/string-vs-array-concat/2).

```javascript
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='syntax'>Синтаксис</a>

![javascript-syntax](https://cloud.githubusercontent.com/assets/2854701/3838799/c95fadf4-1e08-11e4-9a61-54a05120db35.png)


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

- Используйте табуляцию вместо пробелов, т.к. размер можно настроить на своё усмотрение в любом редакторе кода (размер табуляции по умолчанию 4).

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

  // плохо
  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`, используйте название от самого класса в camelCase, вместо `self`/`that`/`_this`/`me` и т.п. Навеяно [отсюда](https://gist.github.com/subzey/8115612).

  ```javascript
  function Rocket() {}

  // ...

  Rocket.prototype.startMessage = 'Houston, I\'m flying into space!';

  // Плохо
  Rocket.prototype.start = function () {
    var _this = this;

    _this.lightsCameraAction(function () {
      console.log(_this.startMessage);
    });
  };

  // Хорошо
  Rocket.prototype.start = function () {
    var rocket = this;

    rocket.lightsCameraAction(function () {
      console.log(rocket.startMessage);
    });
  };
  ```

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

  ```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, listingId) {
       //делаем что-нибудь с listing, например:
       listing.name = listings[listingId]
  });
  ```

  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');
  ```

- Скрипт должен инициализироваться после готовности `DOM`.
```javascript
// Плохо
$('#login').on('submit', function () {
  // Do somthing
});

// Хорошо
$(document).on('ready', function () {
  // Do somthing
});

// Лучше
$(function () {
  // Do somthing
});
  • Кэшируйте jQuery-запросы. Каждый новый jQuery-запрос делает повторный поиск по DOM-дереву, и приложение начинает работать медленнее.

    // плохо
    function setSidebar() {
      $("#sidebar").hide();
    
      // ...код...
    
      $("#sidebar").css({
        backgroundColor: "pink"
      });
    }
    
    // хорошо
    function setSidebar() {
      var $sidebar = $("#sidebar");
      $sidebar.hide();
    
      // ...код...
    
      $sidebar.css({
        backgroundColor: "pink"
      });
    }
  • Для DOM-запросов используйте классический каскадный CSS-синтаксис $('.sidebar ul') или родитель > потомок $('.sidebar > ul'). jsPerf

  • Используйте find для поиска внутри DOM-объекта.

    // плохо
    $("ul", "#sidebar").hide();
    
    // плохо
    $("#sidebar")
      .find("ul")
      .hide();
    
    // хорошо
    $("#sidebar ul").hide();
    
    // хорошо
    $("#sidebar > ul").hide();
    
    // хорошо
    $sidebar.find("ul").hide();
  • Для поиска одного элемента используйте только идентификатор #id.

// плохо
$(".menu button");

// плохо
$(".menu button#menuToggler");

// плохо
$("button#menuToggler");

// хорошо
$("#menuToggler");
  • Для поиска нескольких элементов однотипных по функционалу используйте класс с префиксом .js-* (для разделения названия класса использовать только дефис -), а не класс для стилизации элемента, название класса должно быть небольшим, но и понятным.
// плохо
$(".navbar-menu__item");

// плохо
$(".js-navbar-menu__item");

// хорошо
$(".js-nav-link");
**[[⬆]](#Оглавление)**
  • Для задания обработчика элементу используйте метод .on().
// Плохо
$input
  .click(function() {
    /* ... */
  })
  .focus(function() {
    /* ... */
  })
  .blur(function() {
    /* ... */
  });

// Хорошо
$input
  .on("click", function() {
    /* ... */
  })
  .on("focus", function() {
    /* ... */
  })
  .on("blur", function() {
    /* ... */
  });

// Лучше
// Несколько событий разделяются пробелами
$field.on("click focus", function() {
  /* ... */
});

$input.on({
  // Несколько событий разделяются пробелами
  "click focus": function() {
    /* ... */
  },
  blur: function() {
    /* ... */
  }
});
  • Для корректной рабоспособности должен быть правильный порядок зависимостей (фреймворков/библиотек/плагинов).
  • Своевременно обновляйте зависимости для устранения ошибок и добавления новых фич.
  • Не используйте устаервшие и неподдерживамые скрипты.
  • Не используйте jQuery версии 2.x, если поддерживается Internet Explorer 8 и ниже, для этого есть версия 1.x.
  • Для современных веб-приложений используйте последнюю версию, если нет проблем с совместимостью плагинов и поддерживаются только современные браузеры.

[⬆]

  • Да.

    function() {
      return true;
    }

    [⬆]

[⬆]

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

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

Другие стили

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

Книги

Блоги

[⬆]

[⬆]