Перевод и расширение Airbnb JavaScript Style Guide
- Типы
- Объекты
- Массивы
- Строки
- Функции
- Свойства
- Переменные
- Области видимости
- Условные выражения и равенства
- Блоки кода
- Комментарии
- Пробелы
- Запятые
- Точки с запятой
- Приведение типов
- Соглашение об именовании
- Геттеры и сеттеры
- Конструкторы
- События
- Модули
- jQuery
- Совместимость с ES5
- Тестирование
- Быстродействие
- Ресурсы
- В реальном мире
- Переводы
- Лицензия
-
Примитивы: Когда вы взаимодействуете с примитивом, вы взаимодействуете непосредственно с его значением в памяти.
stringnumberbooleannullundefined
var foo = 1 , bar = foo; bar = 9; console.log(foo, bar); // => 1, 9. foo не изменился
-
Сложные типы: Когда вы взаимодействуете со сложным типом, вы взаимодействуете с ссылкой на его значение в памяти.
objectarrayfunction
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; }; // объявление функции, которая сразу же выполняется (IIF) (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() { var name; if (!arguments.length) { return false; } 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 не яв 5276 яется функцией и не может быть вызвана 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 nope() { return false; } // хорошо function nope() { 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; }
**[[⬆] Содержание](#TOC)**
## <a name='whitespace'>Пробелы</a>
- Используйте программную табуляцию (ее поддерживают все современные редакторы кода и IDE) из двух пробелов.
```javascript
// плохо
function() {
∙∙∙∙var name;
}
// плохо
function() {
∙var name;
}
// хорошо
function() {
∙∙var name;
}
```
- Устанавливайте один пробел перед открывающей скобкой.
```javascript
// плохо
function test(){
console.log('test');
}
var anotherTest = function(){
console.log('test');
};
// хорошо
function test() {
console.log('test');
}
var anotherTest = function () {
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);
```
**[[⬆] Содержание](#TOC)**
## <a name='commas'>Запятые</a>
- Запятые в начале строки: **Да.** — для списка переменных.
```javascript
// bad
var once,
upon,
aTime;
// good
var once
, upon
, aTime