Гайд по стилю кода на JavaScript от AirBnB


::: разработка, фронтенд, javascript, стиль-кода

Перевод AirBnB Style Guide — [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript) (en) на русский язык от команды Uprock. За что им огромное спасибо! Подробнее о переводе на хабре.

Стиль вашего кода, может сказать о вас достаточно много. Это один из показателей опытного разработчика. Помимо исключительно визуальной составляющей, в переводе содержатся бенчмарки и рекомендации по производительности. А в некоторых случаях, использование такого кодстайла, может избавить вас он проблем связанных с тонкостями языка. Статья является отличным дополнением к [вопросам для собеседования кандидату на должность Front-end разработчика](/frontend/front_end_developer_interview_quersions/) и к вашему скиллу как разработчика 😉

Оглавление

  1. [Типы](#types)
  2. [Объекты](#objects)
  3. [Массивы](#arrays)
  4. [Строки](#strings)
  5. [Функции](#functions)
  6. [Свойства](#properties)
  7. [Переменные](#variables)
  8. [Области видимости](#hoisting)
  9. [Условные выражения и равенства](#conditionals)
  10. [Блоки кода](#blocks)
  11. [Комментарии](#comments)
  12. [Пробелы](#whitespace)
  13. [Запятые](#commas)
  14. [Точки с запятой](#semicolons)
  15. [Приведение типов](#type-coercion)
  16. [Соглашение об именовании](#naming-conventions)
  17. [Геттеры и сеттеры](#accessors)
  18. [Конструкторы](#constructors)
  19. [События](#events)
  20. [Модули](#modules)
  21. [jQuery](#jquery)
  22. [Совместимость с ES5](#es5)
  23. [Тестирование](#testing)
  24. [Быстродействие](#performance)
  25. [Ресурсы](#resources)
  26. [В реальном мире](#in-the-wild)

Типы

  • Простые типы: Когда вы взаимодействуете с простым типом, вы взаимодействуете непосредственно с его значением в памяти.

    • 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 = {};

  • Не используйте [зарезервированные слова](http://es5.github.io/#x7.6.1) в качестве ключей объектов. Они не будут работать в IE8. [Подробнее](https://github.com/airbnb/javascript/issues/61)

    // плохо

    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](https://jsperf.com/converting-arguments-to-an-array/7)

    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 и [Обсуждение](https://github.com/airbnb/javascript/issues/40)

    // плохо

    var errorMessage = 'Эта сверхдлинная ошибка возникла из-за белой обезьяны. Не говори про обезъяну! Не слушай об обезьяне! Не думай об обезъяне!< span class="pds">';

    // плохо var errorMessage = 'Эта сверхдлинная ошибка возникла из-за белой обезьяны. </span> Не говори про обезъяну! Не слушай об обезьяне! </span> Не думай об обезъяне!';

    // хорошо var errorMessage = 'Эта сверхдлинная ошибка возникла из-за белой обезьяны. ' + 'Не говори про обезъяну! Не слушай об обезьяне! ' + 'Не думай об обезъяне!';

  • Когда строка создается программным путем, используйте Array: join вместо объединения строк. В основном для IE: [jsPerf](https://jsperf.com/string-vs-array-concat/2).

    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>< span class="pds">') + '</li></ul>< span class="pds">'; }

    [⬆]

Функции

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

    // объявление анонимной функции

    var anonymous = function() { return true; };

    // объявление именованной функции var named = function named() { return true; };

    // объявление функции, которая сразу же выполняется (замыкание) (function() { console.log('Если вы читаете это, вы открыли консоль.'); })();

  • Никогда не объявляйте функцию внутри блока кода — например в if, while, else и так далее. Единственное исключение  — блок функции. Вместо этого присваивайте функцию уже объявленной через var переменной. Условное объявление функций работает, но в различных браузерах работает по-разному.
  • Примечание ECMA-262 устанавливает понятие блока как списка операторов. Объявление функции (не путайте с присвоением функции переменной) не является оператором. [Комментарий по этому вопросу в ECMA-262](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97).

    // плохо

    if (currentUser) { function test() { console.log('Плохой мальчик.< span class="pds">'); } }

    // хорошо 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('анонимная функция< span class="pds">'); }; }

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

    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](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting) от [Ben Cherry](http://www.adequatelygood.com/)

    [⬆]

Условные выражения и равенства

  • Используйте === и !== вместо == и !=.
  • Условные выражения вычисляются посредством приведения к логическому типу 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](https://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/) от Angus Croll

    [⬆]

Блоки кода

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

    // плохо

    if (test) return false;

    // хорошо if (test) return false;

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

    // плохо function() { return false; }

    // хорошо function() { return false; }

    [⬆]

< a id="comments">Комментарии

  • Используйте /** ... */ для многострочных комментариев. Включите описание, опишите типы и значения для всех параметров и возвращаемых значений в формате 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('проверяем тип...< span class="pds">'); // задаем тип по умолчанию 'no type' var type = this._type || 'no type';

    return type; }

    // хорошо function getType() { console.log('проверяем тип...< span class="pds">');

    // задаем тип по умолчанию '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; }

    [⬆]

Пробелы

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

    // плохо

    function() { ∙∙∙∙var name; }

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

    // хорошо function() { ∙∙var name; }

  • Устанавливайте один пробел перед открывающей скобкой.

    // плохо

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

  • Оставляйте новую строку в конце файла.

    // плохо

    (function(global) { // ...код... })(this);

    // хорошо

    (function(global) { // ...код... })(this);

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

    // плохо

    $('#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);

    [⬆]

Запятые

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

    // плохо

    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, но некоторые реализации до этого некорректно разрешали этот вопрос.

      // плохо

    var hero = { firstName: 'Kevin', lastName: 'Flynn', };

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

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

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

    [⬆]

Точки с запятой

  • Да.

    // плохо

    (function() { var name = 'Skywalker' return name })()

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

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

    [⬆]

Приведение типов

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

    //  => this.reviewScore = 9;

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

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

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

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

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

    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 тратится больше всего ресурсов, используйте побитовый сдвиг [из соображений быстродействия](https://jsperf.com/coercion-vs-casting/3), но обязательно оставьте комментарий с объяснением причин.

    // хорошо

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

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

    2147483647 >> 0 //=> 2147483647

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

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

    var age = 0;

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

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

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

    [⬆]

Соглашение об именовании

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

    // плохо

    function q() { // ...код... }

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

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

    // плохо

    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 для именования конструкторов классов

    // плохо

    function user(options) { this.name = options.name; }

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

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

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

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

    // плохо

    this.firstName = 'Panda'; this.firstName_ = 'Panda';

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

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

    // плохо

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

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

    // плохо

    var log = function(msg) { console.log(msg); };

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

    [⬆]

Геттеры и сеттеры: функции для доступа к значениям объекта

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

    // плохо

    dragon.age();

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

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

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

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

    // плохо

    if (!dragon.age()) { return false; }

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

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

    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]; };

    [⬆]

Конструкторы

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

    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 для создания цепочек вызовов. Но стоит оставаться последовательным и обеспечить одинаковое поведение для всех методов, кроме геттеров.

    // плохо

    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 (), но убедитесь, что он работает и не вызывает побочных эффектов.

    function Jedi(options) {

    options || (options = {}); this.name = options.name < span class="k">|| 'no name'; }

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

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

    [⬆]

События

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

    // плохо

    $(this).trigger('listingUpdated', listing.id);

    ...

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

    prefer:

    // хорошо

    $(this).trigger('listingUpdated', { listingId : listing.id });

    ...

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

    [⬆]

Модули

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

    // 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);

    [⬆]

jQuery

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

    // плохо

    var sidebar = $('.sidebar');

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

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

    // плохо

    function setSidebar() { $('.sidebar').hide();

    // ...код...

    $('.sidebar').css({ 'background-color': < span class="s">'pink' }); }

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

    // ...код...

    $sidebar.css({ 'background-color': < span class="s">'pink' }); }

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

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

    // плохо

    $('ul', '.sidebar').hide();

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

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

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

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

    [⬆]

Совместимость ECMAScript 5

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

    [⬆]

Тестирование

  • Да.

    function() {

    return true; }

    [⬆]

Быстродействие

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

    [⬆]

Ресурсы

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

  • [Annotated ECMAScript 5.1](http://es5.github.io/)

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

  • [Google JavaScript Style Guide](https://google.github.io/styleguide/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](http://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](https://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…](https://javascriptweblog.wordpress.com/)
  • [Bocoup Weblog](http://weblog.bocoup.com/)
  • [Adequately Good](http://www.adequatelygood.com/)
  • [NCZOnline](https://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)

    [⬆]

В реальном мире

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

  • Aan Zee: [AanZee/javascript](https://github.com/AanZee/javascript)
  • Airbnb: [airbnb/javascript](https://github.com/airbnb/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)
  • Razorfish: [razorfish/javascript-style-guide](https://github.com/razorfish/javascript-style-guide)
  • Shutterfly: [shutterfly/javascript](https://github.com/shutterfly/javascript)
  • Zillow: [zillow/javascript](https://github.com/zillow/javascript)
  • ZocDoc: [ZocDoc/javascript](https://github.com/ZocDoc/javascript)

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