JavaScript Style Guide - Lebedancer/component-example GitHub Wiki
- Типы
- Объекты
- Массивы
- Строки
- Функции
- Свойства
- Переменные
- Область видимости и "поднятие"
- Условные выражения и равенства
- Блоки кода
- Комментарии
- Пробелы
- Запятые
- Точки с запятой
- Приведение типов
- Соглашения об именовании
- Функции доступа get и set
- Конструкторы
- События
- Модули
- jQuery
Изменение встроенных объектов, таких как Object.prototype и Array.prototype, запрещено. Изменение других втроенных объектов, так как Function.prototype, менее опасно, но все же приводят с сложностям при разработке, и его следует избегать.
-
Простые типы: Когда вы взаимодействуете с простым типом, вы работаете непосредственно с его значением
string
number
boolean
null
undefined
var foo = 1; var bar = foo; bar = 9; console.log(foo, bar); // => 1, 9
-
Сложные типы: Когда вы взаимодействуете со сложным типом, вы работаете со ссылкой на его значение.
object
array
function
var foo = [1, 2]; var bar = foo; bar[0] = 9; console.log(foo[0], bar[0]); // => 9, 9
-
Для создания объекта используйте фигурные скобки
// плохо 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' };
-
Не используйте лишние кавычки
// плохо var superman = { 'attr': 'alien' }; // хорошо var superman = { attr: 'alien' }; // хорошо var superman = { 'data-type': 'alien' };
-
Используйте квадратные скобки для создания массива
// плохо var items = new Array(); // хорошо var items = [];
-
Если длина массива неизвестна, используйте Array#push.
var someStack = []; // плохо someStack[someStack.length] = 'abracadabra'; // хорошо someStack.push('abracadabra');
-
Если необходимо скопировать массив, используйте Array#slice. jsPerf
var len = items.length; var itemsCopy = []; var i; // плохо for (i = 0; i < len; i++) { itemsCopy[i] = items[i]; } // хорошо itemsCopy = items.slice();
-
Чтобы создать из похожего на массив объекта массив, используйте Array#slice.
function trigger() { var args = Array.prototype.slice.call(arguments); ... }
-
Используйте одинарные кавычки
''
для строк// плохо var name = "Bob Parr"; // хорошо var name = 'Bob Parr'; // плохо var fullName = "Bob " + this.lastName; // хорошо var fullName = 'Bob ' + this.lastName;
-
Строки длиннее 80 символов нужно разделять, выполняя перенос через конкатенацию строк.
-
Примечание: строки с большим количеством конкатенаций могут отрицательно влиять на быстродействие. jsPerf и обсуждение
// плохо var errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.'; // плохо var errorMessage = 'This is a super long error that \ was thrown because of Batman. \ When you stop to think about \ how Batman had anything to do \ with this, you would get nowhere \ fast.'; // хорошо var errorMessage = 'This is a super long error that ' + 'was thrown because of Batman. ' + 'When you stop to think about ' + 'how Batman had anything to do ' + 'with this, you would get nowhere ' + 'fast.';
-
Когда строка создается программным путем, используйте Array#join вместо объединения строк. В основном для IE:jsPerf.
var items; var messages; var length; var i; messages = [{ state: 'success', message: 'This one worked.' }, { state: 'success', message: 'This one worked as well.' }, { state: 'error', message: 'This one did not work.' }]; 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; }; // объявление функции, которая сразу же выполняется (IIFE) (function() { console.log('Welcome to the Internet. Please follow me.'); })();
-
Старайтесь иметь только 1 точку выхода из функции:
// плохо
function() {
if(true) {
return true;
} else if(true) {
return false;
} else {
return true;
}
};
// хорошо
function() {
var result;
if(true) {
result = true;
} else if(true) {
result = false;
} else {
result = true;
}
return result;
};
```
- Допускается иметь несколько точек выхода:
1. Return в начале метода, для моментального выхода из функции.
1. Пара return стоит близко друг к другу.
- Никогда не объявляйте функцию внутри блока кода - не функции (if, while, else и т.д.). Вместо этого присваивайте функцию уже объявленной через var переменной. Это будет работать, но в различных браузерах по-разному.
- **Примечание :** ECMA-262 устанавливает понятие блока как списка операторов. Объявление функции не является оператором. [Комментарий по этому вопросу в ECMA-262](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97).
```javascript
// плохо
if (currentUser) {
function test() {
console.log('Nope.');
}
}
// хорошо
var test;
if (currentUser) {
test = function test() {
console.log('Yup.');
};
}
```
- Никогда не называйте параметр функции `arguments`, он будет более приоритетным над объектом arguments, который доступен для каждой функции.
```javascript
// плохо
function nope(name, options, arguments) {
// ...код...
}
// хорошо
function yup(name, options, args) {
// ...код...
}
```
**[⬆](#TOC)**
## <a name='properties'>Свойства</a>
- Используйте точечную нотацию для доступа к свойствам и методам.
```javascript
var luke = {
jedi: true,
age: 28
};
// плохо
var isJedi = luke['jedi'];
// хорошо
var isJedi = luke.jedi;
```
- Используйте квадратные скобки для доступа к свойству, имя которого хранится в переменной.
```javascript
var luke = {
jedi: true,
age: 28
};
function getProp(prop) {
return luke[prop];
}
var isJedi = getProp('jedi');
```
**[⬆](#TOC)**
## <a name='variables'>Переменные</a>
- Всегда используйте `var` для объявления переменных. В противном случае переменная будет объявлена глобальной. Необходимо избегать загрязнения глобального пространства имен.
```javascript
// плохо
superPower = new SuperPower();
// хорошо
var superPower = new SuperPower();
```
- Используйте `var` для каждой переменной и объявляйте каждую переменную на новой строке.
```javascript
// плохо
var items = getItems(),
goSportsTeam = true,
dragonball = 'z';
// хорошо
var items = getItems();
var goSportsTeam = true;
var dragonball = 'z';
```
- Объявляйте переменные, которым не присваивается значение, последними. Это удобно, когда вам необходимо будет задать значение одной из этих переменных в зависимости от уже присвоенных значений предыдущих переменных.
```javascript
// плохо
var i;
var len;
var dragonball;
var items = getItems();
var goSportsTeam = true;
// плохо
var i;
var items = getItems();
var dragonball;
var goSportsTeam = true;
var len;
// хорошо
var items = getItems();
var goSportsTeam = true;
var dragonball;
var len;
var i;
```
- Присваивайте переменные в начале области видимости. Это помогает избегать проблем с объявлением переменных и областями видимости.
```javascript
// плохо
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;
}
```
**[⬆](#TOC)**
## <a name='hoisting'>Область видимости и "поднятие"</a>
- Объявление переменных "поднимается" в начало области видимости, а присвоение — нет.
```javascript
// Мы знаем, что это не будет работать
// если нет глобальной переменной notDefined
function example() {
console.log(notDefined); // => throws a ReferenceError
}
// Объявление переменной после попытки ее использования
// будет работать по правилу "поднятия"
// Заметьте, что присвоение значения `true` не поднимается
function example() {
console.log(declaredButNotAssigned); // => undefined
var declaredButNotAssigned = true;
}
// Интерпретатор переносит объявление переменной
// к началу области видимости.
// Что значит, что предыдущий пример будет воспринят так:
function example() {
var declaredButNotAssigned;
console.log(declaredButNotAssigned); // => undefined
declaredButNotAssigned = true;
}
```
- Объявление анонимной функции поднимает наверх области видимости переменную, но не ее значение.
```javascript
function example() {
console.log(anonymous); // => undefined
anonymous(); // => TypeError anonymous is not a function
var anonymous = function() {
console.log('anonymous function expression');
};
}
```
- Объявление именованной функции поднимает переменную, не ее имя функции или ее тело.
```javascript
function example() {
console.log(named); // => undefined
named(); // => TypeError named is not a function
superPower(); // => ReferenceError superPower is not defined
var named = function superPower() {
console.log('Flying');
};
}
// То же самое происходит, когда имя функции и имя переменной совпадают.
function example() {
console.log(named); // => undefined
named(); // => TypeError named is not a function
var named = function named() {
console.log('named');
}
}
```
- Объявления функции поднимают и имя, и тело функции.
```javascript
function example() {
superPower(); // => Flying
function superPower() {
console.log('Flying');
}
}
```
- Подробнее можно прочитать в статье [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting) by [Ben Cherry](http://www.adequatelygood.com/)
**[⬆](#TOC)**
## <a name='conditionals'>Условные выражения и равенства</a>
- Используйте `===` и`!==` вместо `==` и `!=`.
- Условные выражения вычисляются посредством приведения через метод `ToBoolean` и всегда следуют следующим правилам:
+ **Objects** соответствует **true**
+ **Undefined** соответствует **false**
+ **Null** соответствует **false**
+ **Booleans** остаются неизменными
+ **Numbers** соответствует **false** если имеют значения **+0, -0, или NaN**, иначе **true**
+ **Strings** соответствует **false** если является пустой строкой `''`, иначе **true**
```javascript
if ([0]) {
// true
// Массив(Array) является объектом, объекты преобразуются в true
}
```
- Используйте короткий синтаксис.
```javascript
// плохо
if (name !== '') {
// ...код...
}
// хорошо
if (name) {
// ...код...
}
// плохо
if (collection.length > 0) {
// ...код...
}
// хорошо
if (collection.length) {
// ...код...
}
```
- Подробнее можно прочитать в статье [Truth Equality and JavaScript](http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108) by Angus Croll
**[⬆](#TOC)**
## <a name='blocks'>Блоки кода</a>
- Используйте фигурные скобки для всех многострочных блоков.
```javascript
// плохо
if (test)
return false;
// плохо
if (test) return false;
// хорошо
if (test) {
return false;
}
// плохо
function() { return false; }
// хорошо
function() {
return false;
}
```
**[⬆](#TOC)**
## <a name='comments'>Комментарии</a>
- Используйте `/** ... */` для многострочных комментариев. Включите описание, опишите типы и значения для всех параметров и возвращаемых значений.
```javascript
// плохо
// make() возвращает новый элемент
// основываясь на получаемом имени тэга
//
// @param <String> tag
// @return <Element> element
function make(tag) {
// ...код...
return element;
}
// хорошо
/**
* make() возвращает новый элемент
* основываясь на получаемом имени тэга
*
* @param <String> tag
* @return <Element> element
*/
function make(tag) {
// ...код...
return element;
}
```
- Используйте `//` для однострочных комментариев. Размещайте однострочные комментарии на новой строке над темой комментария. Добавляйте пустую строку над комментарием.
```javascript
// плохо
var active = true; // is current tab
// хорошо
// is current tab
var active = true;
// плохо
function getType() {
console.log('fetching type...');
// set the default type to 'no type'
var type = this._type || 'no type';
return type;
}
// хорошо
function getType() {
console.log('fetching type...');
// set the default type to 'no type'
var type = this._type || 'no type';
return type;
}
```
- Префикс `FIXME` или `TODO` помогает другим разработчикам быстро понять, что вы указываете на проблему, к которой нужно вернуться в дальнейшем, или если вы предлагаете решение проблемы, которое должно быть реализовано. Эти комментарии отличаются от обычных, так как они призывают к действию: `FIXME -- нужно разобраться`, `TODO -- нужно реализовать`.
- Используйте `// FIXME:` чтобы указать на проблему
```javascript
function Calculator() {
// FIXME: не нужно использовать глобальную переменную
total = 0;
return this;
}
```
- Use `// TODO:` для указания решения проблемы
```javascript
function Calculator() {
// TODO: должна быть возможность изменять значение через параметр функции
this.total = 0;
return this;
}
**[⬆](#TOC)**
-
Используйте табуляцию из 4 пробелов
// плохо 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' });
-
Выделяйте операторы пробелами.
// плохо var x=y+5; // хорошо var x = y + 5;
-
Отделяйте содержимое объекта от скобок пробелами. При написании объекта в строку.
// плохо {name: 'Vasya'} // хорошо { name: 'Vasya' }
-
Используйте отступы, когда делаете цепочки вызовов.
// плохо $('#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' };
-
Дополнительная запятая в конце объектов: Нет.
// Плохо const hero = { firstName: 'Dana', lastName: 'Scully', }; const heroes = [ 'Batman', 'Superman', ]; // Хорошо const hero = { firstName: 'Dana', lastName: 'Scully' }; const heroes = [ 'Batman', 'Superman' ];
-
Да.
// плохо (function() { var name = 'Skywalker' return name })() // хорошо (function() { var name = 'Skywalker'; return name; })();
-
Для плагинов jQuery.
// хорошо ;(function() { var name = 'Skywalker'; return name; })();
-
Выполняйте приведение типов в начале операции.
-
Строки:
// => this.reviewScore = 9; // плохо var totalScore = this.reviewScore + ''; // хорошо var totalScore = '' + this.reviewScore; // плохо var totalScore = '' + this.reviewScore + ' total score'; // хорошо var totalScore = this.reviewScore + ' total score';
-
Используйте
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 тратится больше всего ресурсов, используйте побитовый сдвиг из соображений быстродействия, но обязательно оставьте комментарий с объяснением причин.
-
Примечание: Будьте осторожны с побитовыми операциями. Числа в JavaScript являются 64-битными значениями, но побитовые операции всегда возвращают 32-битные значения. (Источник). Побитовые операции над числами, значение которых выходит за 32 бита, могут привести к неожиданному поведению. Обсуждение
// хорошо /** * этот код медленно работал из-за parseInt * побитовый сдвиг строки для приведения ее к числу * работает значительно быстрее. */ var val = inputValue >> 0;
-
Логические типы:
var age = 0; // плохо var hasAge = new Boolean(age); // хорошо var hasAge = !!age; // хорошо var hasAge = Boolean(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' });
-
Используйте camelCase для именования событий
// плохо var $button = $('button'); $button.trigger('PressMeSoftly'); // плохо var $button = $('button'); $button.trigger('press_Me_Softly'); // хорошо var $button = $('button'); $button.trigger('pressMeSoftly');
-
Используйте PascalCase для именования конструкторов или классов
// плохо function user(options) { this.name = options.name; } var bad = new user({ name: 'nope' }); // хорошо function User(options) { this.name = options.name; } var good = new User({ name: 'yup' });
-
Все приватные свойства должны быть внутри замыкания.
// плохо this.__firstName__ = 'Panda'; this.firstName_ = 'Panda'; // хорошо function animals() { var firstName = 'Panda'; return { setFirstName: function (firstName) { firstName = firstName; }, getFirstName: function() { return firstName; } } }
-
Сохраняя ссылку на this, используйте смысл
this
.// плохо function() { var that = this; return function() { console.log(that); }; } // плохо function() { var _this = this; return function() { console.log(_this); }; } // приемлемо function() { var self = this; return function() { console.log(self); }; } // хорошо function createPlugin() { var createPluginContext = this; return function() { console.log(createPluginContext); }; }
-
Задавайте имена для функций. Это полезно в сообщениях об ошибках(рекомендация).
// плохо 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 || 'no name'; } Jedi.prototype.getName = function getName() { return this.name; }; Jedi.prototype.toString = function toString() { return 'Jedi - ' + this.getName(); };
-
Подключая набор данных к событиям (как DOM-событиям, так и более частным, например, в Backbone), передавайте объект вместо простой переменной. Это позволяет впоследствии добавлять больше данных в объект события без переписывания хэндлеров.
// плохо $(this).trigger('listingUpdated', listing.id); ... $(this).on('listingUpdated', function(e, listingId) { // do something with listingId });
// хорошо $(this).trigger('listingUpdated', { listingId : listing.id }); ... $(this).on('listingUpdated', function(e, data) { // do something with data.listingId });
-
Модуль должен начинаться с
!
. За счет этого даже некорректно сформированный модуль, в конце которого отсутствует точка с запятой, не вызовет ошибок при сборке скриптов. Объяснение -
Файл должен быть именован с 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-переменных используйте префикс
$
.// плохо var sidebar = $('.sidebar'); // хорошо var $sidebar = $('.sidebar');
-
Кэшируйте jQuery-запросы.
// плохо function setSidebar() { $('.sidebar').hide(); // ...код... $('.sidebar').css({ 'background-color': 'pink' }); } // хорошо function setSidebar() { var $sidebar = $('.sidebar'); $sidebar.hide(); // ...код... $sidebar.css({ 'background-color': 'pink' }); }
-
Для DOM-запросов используйте каскадный синтаксис
$('.sidebar ul')
или родитель > потомок$('.sidebar > ul')
. jsPerf -
Используйте
find
для поиска внутри DOM-объекта.// плохо $('ul', '.sidebar').hide(); // плохо $('.sidebar').find('ul').hide(); // хорошо $('.sidebar ul').hide(); // хорошо $('.sidebar > ul').hide(); // хорошо $sidebar.find('ul').hide();