436 lines
22 KiB
Markdown
436 lines
22 KiB
Markdown
# Советы по стилю кода
|
||
|
||
Код должен быть максимально читаемым и понятным. Для этого нужен *хороший стиль* написания кода. В этой главе мы рассмотрим компоненты такого стиля.
|
||
|
||
[cut]
|
||
## Синтаксис
|
||
|
||
Шпаргалка с правилами синтаксиса (детально они их варианты разобраны далее):
|
||
|
||
|
||
<img src="code-style.svg">
|
||
|
||
<!--
|
||
```js
|
||
function pow(x, n) {
|
||
var result = 1;
|
||
|
||
for (var i = 0; i < n; i++) {
|
||
result *=x;
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
var x = prompt("x?", "");
|
||
var n = prompt("n?", "");
|
||
|
||
if (n < 0) {
|
||
alert('Степень ' + n +
|
||
'не поддерживается, введите целую степень, большую 0');
|
||
} else {
|
||
alert( pow(x, n) );
|
||
}
|
||
```
|
||
-->
|
||
|
||
|
||
Не всё здесь однозначно, так что разберём эти правила подробнее.
|
||
|
||
### Фигурные скобки
|
||
|
||
Пишутся на той же строке, так называемый "египетский" стиль. Перед скобкой -- пробел.
|
||
|
||
<!--
|
||
```js
|
||
if (n < 0) {alert('Степень ' + n + ' не поддерживается');}
|
||
|
||
|
||
|
||
if (n < 0) alert('Степень ' + n + ' не поддерживается');
|
||
|
||
|
||
|
||
if (n < 0) {
|
||
alert('Степень ' + n + ' не поддерживается');
|
||
}
|
||
|
||
```
|
||
-->
|
||
|
||
<img src="figure-bracket-style.svg">
|
||
|
||
Если у вас уже есть опыт в разработке и вы привыкли делать скобку на отдельной строке -- это тоже вариант. В конце концов, решать вам. Но в большинстве JavaScript-фреймворков стиль именно такой.
|
||
|
||
Если условие и код достаточно короткие, например `if (cond) return null`, то запись в одну строку вполне читаема... Но, как правило, отдельная строка всё равно воспринимается лучше.
|
||
|
||
### Длина строки
|
||
|
||
Максимальную длину строки согласовывают в команде. Как правило, это либо `80`, либо `120` символов, в зависимости от того, какие мониторы у разработчиков.
|
||
|
||
Более длинные строки необходимо разбивать для улучшения читаемости.
|
||
|
||
### Отступы
|
||
|
||
Отступы нужны двух типов:
|
||
|
||
<ul>
|
||
<li>**Горизонтальный отступ, при вложенности -- два(или четыре) пробела.**
|
||
|
||
Как правило, используются именно пробелы, т.к. они позволяют сделать более гибкие "конфигурации отступов", чем символ "Tab".
|
||
|
||
Например, выровнять аргументы относительно открывающей скобки:
|
||
```js
|
||
show("Строки" +
|
||
" выровнены" +
|
||
" строго" +
|
||
" одна под другой");
|
||
```
|
||
</li>
|
||
<li>**Вертикальный отступ, для лучшей разбивки кода -- перевод строки.**
|
||
|
||
Используется, чтобы разделить логические блоки внутри одной функции. В примере разделены инициализация переменных, главный цикл и возвращение результата:
|
||
|
||
```js
|
||
function pow(x, n) {
|
||
var result = 1;
|
||
// <--
|
||
for (var i = 0; i < n; i++) {
|
||
result *=x;
|
||
}
|
||
// <--
|
||
return result;
|
||
}
|
||
|
||
```
|
||
|
||
Вставляйте дополнительный перевод строки туда, где это сделает код более читаемым. Не должно быть более 9 строк кода подряд без вертикального отступа.
|
||
</li>
|
||
</ul>
|
||
|
||
### Точка с запятой
|
||
|
||
Точки с запятой нужно ставить, даже если их, казалось бы, можно пропустить.
|
||
|
||
Есть языки, в которых точка с запятой не обязательна, и её там никто не ставит. В JavaScript перевод строки её заменяет, но лишь частично, поэтому лучше её ставить, как обсуждалось [ранее](#semicolon).
|
||
|
||
## Именование
|
||
|
||
Общее правило:
|
||
<ul>
|
||
<li>Имя переменной -- существительное.</li>
|
||
<li>Имя функции -- глагол или начинается с глагола. Бывает, что имена для краткости делают существительными, но глаголы понятнее.</li>
|
||
</ul>
|
||
|
||
Для имён используется английский язык (не транслит) и верблюжья нотация.
|
||
|
||
Более подробно -- читайте про [имена функций](#function-naming) и [имена переменных](#variable-naming).
|
||
|
||
## Уровни вложенности
|
||
|
||
Уровней вложенности должно быть немного.
|
||
|
||
Например, [проверки в циклах можно делать через "continue"](#continue), чтобы не было дополнительного уровня `if(..) { ... }`:
|
||
|
||
Вместо:
|
||
|
||
```js
|
||
for (var i=0; i<10; i++) {
|
||
if (i подходит) {
|
||
... // <- уровень вложенности 2
|
||
}
|
||
}
|
||
```
|
||
|
||
Используйте:
|
||
|
||
```js
|
||
for (var i=0; i<10; i++) {
|
||
if (i *!*не*/!* подходит) *!*continue*/!*;
|
||
... // <- уровень вложенности 1
|
||
}
|
||
```
|
||
|
||
Аналогичная ситуация -- с `if/else` и `return`. Следующие две конструкции идентичны.
|
||
|
||
Первая:
|
||
|
||
```js
|
||
function isEven(n) { // проверка чётности
|
||
if (n % 2 == 0) {
|
||
return true;
|
||
*!*
|
||
} else {
|
||
return false;
|
||
}
|
||
*/!*
|
||
}
|
||
```
|
||
|
||
Вторая:
|
||
|
||
```js
|
||
function isEven(n) { // проверка чётности
|
||
if (n % 2 == 0) {
|
||
return true;
|
||
}
|
||
|
||
*!*
|
||
return false;
|
||
*/!*
|
||
}
|
||
```
|
||
|
||
Если в блоке `if` идёт `return`, то `else` за ним не нужен.
|
||
|
||
**Лучше быстро обработать простые случаи, вернуть результат, а дальше разбираться со сложным, без дополнительного уровня вложенности.**
|
||
|
||
В случае с функцией `isEven` можно было бы поступить и проще:
|
||
|
||
```js
|
||
function isEven(n) { // проверка чётности
|
||
return !(n % 2);
|
||
}
|
||
```
|
||
|
||
...Однако, если код `!(n % 2)` для вас менее очевиден чем предыдущий вариант, то стоит использовать предыдущий.
|
||
|
||
Главное для нас -- не краткость кода, а его простота и читаемость. Совсем не всегда более короткий код проще для понимания, чем более развёрнутый.
|
||
|
||
## Функции = Комментарии
|
||
|
||
Функции должны быть небольшими. Если функция большая -- желательно разбить её на несколько.
|
||
|
||
Этому правилу бывает сложно следовать, но оно стоит того. При чем же здесь комментарии?
|
||
|
||
Вызов отдельной небольшой функции не только легче отлаживать и тестировать -- сам факт его наличия является *отличным комментарием*.
|
||
|
||
Сравните, например, две функции `showPrimes(n)` для вывода простых чисел до `n`.
|
||
|
||
Первый вариант использует метку:
|
||
|
||
```js
|
||
function showPrimes(n) {
|
||
nextPrime:
|
||
for (var i=2; i<n; i++) {
|
||
|
||
for (var j=2; j<i; j++) {
|
||
if ( i % j == 0) continue nextPrime;
|
||
}
|
||
|
||
alert(i); // простое
|
||
}
|
||
}
|
||
```
|
||
|
||
Второй вариант -- дополнительную функцию `isPrime(n)` для проверки на простоту:
|
||
|
||
```js
|
||
function showPrimes(n) {
|
||
|
||
for (var i=2; i<n; i++) {
|
||
*!*if (!isPrime(i)) continue;*/!*
|
||
|
||
alert(i); // простое
|
||
}
|
||
}
|
||
|
||
function isPrime(n) {
|
||
for (var i=2; i<n; i++) {
|
||
if ( n % i == 0) return false;
|
||
}
|
||
return true;
|
||
}
|
||
```
|
||
|
||
Второй вариант проще и понятнее, не правда ли? Вместо участка кода мы видим описание действия, которое там совершается (проверка `isPrime`).
|
||
|
||
## Функции -- под кодом
|
||
|
||
Есть два способа расположить функции, необходимые для выполнения кода.
|
||
|
||
<ol>
|
||
<li>Функции над кодом, который их использует:
|
||
|
||
```js
|
||
// *!*объявить функции*/!*
|
||
function createElement() {
|
||
...
|
||
}
|
||
|
||
function setHandler(elem) {
|
||
...
|
||
}
|
||
|
||
function walkAround() {
|
||
...
|
||
}
|
||
|
||
// *!*код, использующий функции*/!*
|
||
var elem = createElement();
|
||
setHandler(elem);
|
||
walkAround();
|
||
```
|
||
|
||
</li>
|
||
<li>Сначала код, а функции внизу:
|
||
|
||
```js
|
||
// *!*код, использующий функции*/!*
|
||
var elem = createElement();
|
||
setHandler(elem);
|
||
walkAround();
|
||
|
||
// --- *!*функции*/!* ---
|
||
|
||
function createElement() {
|
||
...
|
||
}
|
||
|
||
function setHandler(elem) {
|
||
...
|
||
}
|
||
|
||
function walkAround() {
|
||
...
|
||
}
|
||
```
|
||
|
||
</li>
|
||
</ol>
|
||
|
||
...На самом деле существует еще третий "стиль", при котором функции хаотично разбросаны по коду, но это ведь не наш метод, да?
|
||
|
||
**Как правило, лучше располагать функции под кодом, который их использует.**
|
||
|
||
То есть, предпочтителен 2й способ.
|
||
|
||
Дело в том, что при чтении такого кода мы хотим знать в первую очередь, *что он делает*, а уже затем *какие функции ему помогают.* Если первым идёт код, то это как раз дает необходимую информацию. Что же касается функций, то вполне возможно нам и не понадобится их читать, особенно если они названы адекватно и то, что они делают, понятно из названия.
|
||
|
||
## Плохие комментарии
|
||
|
||
В коде нужны комментарии.
|
||
|
||
Сразу начну с того, каких комментариев быть почти не должно.
|
||
|
||
**Должен быть минимум комментариев, которые отвечают на вопрос "что происходит в коде?"**
|
||
|
||
Что интересно, в коде начинающих разработчиков обычно комментариев либо нет, либо они как раз такого типа: "что делается в этих строках".
|
||
|
||
Серьёзно, хороший код и так понятен.
|
||
|
||
Об этом замечательно выразился Р.Мартин в книге ["Чистый код"](http://www.ozon.ru/context/detail/id/21916535/): "Если вам кажется, что нужно добавить комментарий для улучшения понимания, это значит, что ваш код недостаточно прост, и, может, стоит переписать его".
|
||
|
||
Если у вас образовалась длинная "простыня", то, возможно, стоит разбить её на отдельные функции, и тогда из их названий будет понятно, что делает тот или иной фрагмент.
|
||
|
||
Да, конечно, бывают сложные алгоритмы, хитрые решения для оптимизации, поэтому нельзя такие комментарии просто запретить. Но перед тем, как писать подобное -- подумайте: "Нельзя ли сделать код понятным и без них?"
|
||
|
||
## Хорошие комментарии
|
||
|
||
|
||
А какие комментарии полезны и приветствуются?
|
||
|
||
<ul>
|
||
<li>**Архитектурный комментарий -- "как оно, вообще, устроено".**
|
||
|
||
Какие компоненты есть, какие технологии использованы, поток взаимодействия. О чём и зачем этот скрипт. Взгляд с высоты птичьего полёта. Эти комментарии особенно нужны, если вы не один, а проект большой.
|
||
|
||
Для описания архитектуры, кстати, создан специальный язык [UML](http://ru.wikipedia.org/wiki/Unified_Modeling_Language), красивые диаграммы, но можно и без этого. Главное -- чтобы понятно.
|
||
</li>
|
||
<li>**Справочный комментарий перед функцией -- о том, что именно она делает, какие параметры принимает и что возвращает.**
|
||
|
||
Для таких комментариев существует синтаксис [JSDoc](http://en.wikipedia.org/wiki/JSDoc).
|
||
|
||
```js
|
||
/**
|
||
* Возвращает x в степени n, только для натуральных n
|
||
*
|
||
* @param {number} x Число для возведения в степень.
|
||
* @param {number} n Показатель степени, натуральное число.
|
||
* @return {number} x в степени n.
|
||
*/
|
||
function pow(x, n) {
|
||
...
|
||
}
|
||
```
|
||
|
||
Такие комментарии позволяют сразу понять, что принимает и что делает функция, не вникая в код.
|
||
|
||
Кстати, они автоматически обрабатываются многими редакторами, например [Aptana](http://aptana.com) и редакторами от [JetBrains](http://www.jetbrains.com/), которые учитывают их при автодополнении, а также выводят их в автоподсказках при наборе кода.
|
||
|
||
Кроме того, есть инструменты, например [JSDoc 3](https://github.com/jsdoc3/jsdoc), которые умеют генерировать по таким комментариям документацию в формате HTML. Более подробную информацию об этом можно также найти на сайте [](http://usejsdoc.org/).
|
||
</li>
|
||
</ul>
|
||
|
||
**...Но куда более важными могут быть комментарии, которые объясняют не *что*, а *почему* в коде происходит именно это!**
|
||
|
||
Как правило, из кода можно понять, что он делает. Бывает, конечно, всякое, но, в конце концов, вы этот код *видите*. Однако гораздо важнее может быть то, чего вы *не видите*!
|
||
|
||
*Почему* это сделано именно так? На это сам код ответа не даёт.
|
||
|
||
Например:
|
||
|
||
<dl>
|
||
<dt>Есть несколько способов решения задачи. Почему выбран именно этот?</dt>
|
||
<dd>
|
||
Например, пробовали решить задачу по-другому, но не получилось -- напишите об этом. Почему вы выбрали именно этот способ решения? Особенно это важно в тех случаях, когда используется не первый приходящий в голову способ, а какой-то другой.
|
||
|
||
Без этого возможна, например, такая ситуация:
|
||
<ul>
|
||
<li>Вы открываете код, который был написан какое-то время назад, и видите, что он "неоптимален".</li>
|
||
<li>Думаете: "Какой я был дурак", и переписываете под "более очевидный и правильный" вариант.</li>
|
||
<li>...Порыв, конечно, хороший, да только этот вариант вы уже обдумали раньше. И отказались, а почему -- забыли. В процессе переписывания вспомнили, конечно (к счастью), но результат - потеря времени на повторное обдумывание.</li>
|
||
</ul>
|
||
|
||
Комментарии, которые объясняют выбор решения, очень важны. Они помогают понять происходящее и предпринять правильные шаги при развитии кода.
|
||
</dd>
|
||
<dt>Какие неочевидные возможности обеспечивает этот код? Где ещё они используются?</dt>
|
||
<dd>
|
||
В хорошем коде должно быть минимум неочевидного. Но там, где это есть -- пожалуйста, комментируйте.
|
||
</dd>
|
||
</dl>
|
||
|
||
|
||
[smart header="Комментарии -- это важно"]
|
||
Один из показателей хорошего разработчика -- качество комментариев, которые позволяют эффективно поддерживать код, возвращаться к нему после любой паузы и легко вносить изменения.
|
||
[/smart]
|
||
|
||
## Руководства по стилю
|
||
|
||
Когда написанием проекта занимается целая команда, то должен существовать один стандарт кода, описывающий где и когда ставить пробелы, запятые, переносы строк и т.п.
|
||
|
||
Сейчас, когда есть столько готовых проектов, нет смысла придумывать целиком своё руководство по стилю. Можно взять уже готовое, и которому, по желанию, всегда можно что-то добавить.
|
||
|
||
Большинство есть на английском, сообщите мне, если найдёте хороший перевод:
|
||
|
||
<ul>
|
||
<li>[Google JavaScript Style Guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml)</li>
|
||
<li>[JQuery Core Style Guidelines](http://docs.jquery.com/JQuery_Core_Style_Guidelines)</li>
|
||
<li>[Idiomatic.JS](https://github.com/rwldrn/idiomatic.js) (есть [перевод](https://github.com/rwldrn/idiomatic.js/tree/master/translations/ru_RU))</li>
|
||
<li>[Dojo Style Guide](http://dojotoolkit.org/community/styleGuide)</li>
|
||
</ul>
|
||
|
||
Для того, чтобы начать разработку, вполне хватит элементов стилей, обозначенных в этой главе. В дальнейшем, посмотрев эти руководства, вы можете выработать и свой стиль, но лучше не делать его особенно "уникальным и неповторимым", себе дороже потом будет с людьми сотрудничать.
|
||
|
||
### Автоматизированные средства проверки
|
||
|
||
Существуют средства, проверяющие стиль кода.
|
||
|
||
Самые известные -- это:
|
||
|
||
<ul>
|
||
<li>[JSLint](http://www.jslint.com/) -- проверяет код на соответствие [стилю JSLint](http://www.jslint.com/lint.html), в онлайн-интерфейсе вверху можно ввести код, а внизу различные настройки проверки, чтобы сделать её более мягкой. </li>
|
||
<li>[JSHint](http://www.jshint.com/) -- вариант JSLint с большим количеством настроек.</li>
|
||
<li>[Closure Linter](https://developers.google.com/closure/utilities/) -- проверка на соответствие [Google JavaScript Style Guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml).</li>
|
||
</ul>
|
||
|
||
В частности, JSLint и JSHint интегрированы с большинством редакторов, они гибко настраиваются под нужный стиль и совершенно незаметно улучшают разработку, подсказывая, где и что поправить.
|
||
|
||
Побочный эффект -- они видят некоторые ошибки, например необъявленные переменные. У меня это обычно результат опечатки, которые таким образом сразу отлавливаются. Очень рекомендую поставить что-то из этого. Я использую [JSHint](http://www.jshint.com/).
|
||
|
||
## Итого
|
||
|
||
Описанные принципы оформления кода уместны в большинстве проектов. Есть и другие полезные соглашения.
|
||
|
||
Следуя (или не следуя) им, необходимо помнить, что любые советы по стилю хороши лишь тогда, когда делают код читаемее, понятнее, проще в поддержке.
|
||
|