|
| 1 | +# Стиль кода JS |
| 2 | + |
| 3 | +## Форматирование |
| 4 | +### Строка не должна быть длиннее 80 символов |
| 5 | +Строки более 80 символов длиной, снижают скорость чтения заставляя читатаеля |
| 6 | +перемещать глаза на большое расстояние. |
| 7 | + |
| 8 | +Не стоит забывать и о том, что код, написанный в Академии часто показывается |
| 9 | +в разного рода презентациях, что увеличивает требования к читаемости кода |
| 10 | + |
| 11 | +### Выравнивание знаков равенства запрещено |
| 12 | +Трудоемкий и очень хрупкий процесс. Если хотя бы одна из переменных |
| 13 | + |
| 14 | +### Перенос строк |
| 15 | +#### Блоки кода отделяются двумя пробелами |
| 16 | +Код внутри блока отбивается двумя пробелами относительно начала блока. |
| 17 | +Правило работает даже внутри кода с другим отступом. |
| 18 | +```diff |
| 19 | ++ function factorial(number) { |
| 20 | ++ if (number === 1 || number === 0) { |
| 21 | ++ return 1; |
| 22 | ++ } |
| 23 | ++ |
| 24 | ++ return number * factorial(number - 1); |
| 25 | ++ } |
| 26 | +``` |
| 27 | + |
| 28 | +```diff |
| 29 | ++ document.querySelectorAll('button'). |
| 30 | ++ addEventListener('click', function(evt) { |
| 31 | ++ // Используются два пробела, потому что отступ делается |
| 32 | ++ // внутри тела функции, хоть она и является неперенесенным |
| 33 | ++ // аргументом при вызове addEventListener. |
| 34 | ++ evt.preventDefault(); |
| 35 | ++ ga._trackEvent('click', evt.target.innerText); |
| 36 | ++ }, true); |
| 37 | +``` |
| 38 | + |
| 39 | +#### Аргументы функций и вызовы через цепчку отделяются четырьмя пробелами |
| 40 | +При переносе длинных вызовов функции и обращении к свойствам через точку |
| 41 | +по длинной цепочке, при переносе используются четыре пробела. Меньшее количество |
| 42 | +пробелов сделает отступ неотличимым при быстром чтении кода от отступа |
| 43 | +у вложенного блока. |
| 44 | + |
| 45 | +```diff |
| 46 | ++ <ReactComponent className={classname( |
| 47 | ++ 'base-class', true, |
| 48 | ++ 'base-class--modified', isModificatiorNeeded(), |
| 49 | ++ 'base-class--collapsed', this.isCollapsed())} /> |
| 50 | +``` |
| 51 | + |
| 52 | +#### Альтернативный способ переносить аргументы — выравнивать их с открывающей скобкой |
| 53 | +В определенных случаях аргументы функции можно выравнивать вместе |
| 54 | +с открывающей скобкой вызова функции. Это можно делать в случае, |
| 55 | +если названия аргументов достаточно длинные. |
| 56 | + |
| 57 | +Такой же перенос можно использовать в проверке условий (`if`, `while`). |
| 58 | + |
| 59 | +```diff |
| 60 | ++ while (footer.getBoundingClientRect().top - window.innerHeight > 0 && |
| 61 | ++ currentPageNumber < itemsToShow) { |
| 62 | ++ displayNextPage(); |
| 63 | ++ } |
| 64 | +``` |
| 65 | + |
| 66 | +_В обоих случаях при переносе аргументов функции, закрывающая скобка |
| 67 | +при вызове остается на последней строке._ |
| 68 | + |
| 69 | +#### Объявление объектов и массивов |
| 70 | +При многострочном объявлении массивов и объектов, используется два пробела |
| 71 | +и закрывающая скобка ставится на отдельную строку. |
| 72 | +```diff |
| 73 | ++ var AvailableDirection = [ |
| 74 | ++ Direction.TOP, |
| 75 | ++ Direction.LEFT, |
| 76 | ++ Direction.RIGHT |
| 77 | ++ ]; |
| 78 | +``` |
| 79 | + |
| 80 | +#### В многострочных операциях, операторы ставятся в конце строки, а не в начале |
| 81 | +При переносе длинных вызовов (чейны, объявление массивов и объектов, функции |
| 82 | +с большим количеством аргументов, тернарные операторы) операторы остаются |
| 83 | +на предыдущей строке. |
| 84 | + |
| 85 | +Правила с переносом оператора хорошо работают в языках, где необязательно |
| 86 | +удалять забытые запятые в конце спиков. Но даже в этом случае быстрое удаление |
| 87 | +и сортировка строк не будут работать, потому у списков будет отличаться первая |
| 88 | +строка, а у чейнов — последняя и после сортировки и удаления нужно перепроверить |
| 89 | +получившийся список на валидность. |
| 90 | + |
| 91 | +```diff |
| 92 | ++ document.body. |
| 93 | ++ querySelectorAll('div'). |
| 94 | ++ quertSelectorAll('span'); |
| 95 | + |
| 96 | +- document.body |
| 97 | +- .querySelectorAll('div') |
| 98 | +- .quertSelectorAll('span'); |
| 99 | +``` |
| 100 | + |
| 101 | +```diff |
| 102 | ++ var Direction = { |
| 103 | ++ TOP: 0x01, |
| 104 | ++ BOTTOM: 0x02, |
| 105 | ++ LEFT: 0x04, |
| 106 | ++ RIGHT: 0x08 |
| 107 | ++ }; |
| 108 | + |
| 109 | +- var Direction = { |
| 110 | +- TOP: 0x01 |
| 111 | +- ,BOTTOM: 0x02 |
| 112 | +- ,LEFT: 0x04 |
| 113 | +- ,RIGHT: 0x08 |
| 114 | +- }; |
| 115 | +``` |
| 116 | + |
| 117 | +```diff |
| 118 | ++ var AvailableDirection = [ |
| 119 | ++ Direction.TOP, |
| 120 | ++ Direction.LEFT, |
| 121 | ++ Direction.RIGHT |
| 122 | ++ ]; |
| 123 | + |
| 124 | +- var AvailableDirection = [ |
| 125 | +- Direction.TOP |
| 126 | +- ,Direction.LEFT |
| 127 | +- ,Direction.RIGHT |
| 128 | +- ]; |
| 129 | +``` |
| 130 | + |
| 131 | +Если примеры с объявлением массивов и объектов или с цепочкой вызовов |
| 132 | +объясняются логически, то перенос разделителей в тернарном операторе |
| 133 | +не имеет никакого смысла |
| 134 | + |
| 135 | +```diff |
| 136 | +- var element = 'content' in document.createElement('template') |
| 137 | +- ? template.content.children[0].cloneNode(true) |
| 138 | +- : template.children[0].cloneNode(true); |
| 139 | + |
| 140 | ++ var element = 'content' in document.createElement('template') ? |
| 141 | ++ template.content.children[0].cloneNode(true) : |
| 142 | ++ template.chilren[0].cloneNode(true); |
| 143 | +``` |
| 144 | + |
| 145 | +## Правила языка |
| 146 | +В каждом файле нужно использовать директиву `'use strict'` для включения |
| 147 | +интерпретатора в строгий режим исполнения JavaScript. |
| 148 | + |
| 149 | +### Каждая переменная объявляется через свой var |
| 150 | +Один var для нескольких переменных является одним шагом исполнения, поэтому |
| 151 | +проходится как интерпретатором, так и отладчиком как единственная инструкция, |
| 152 | +даже если объявленных переменных несколько. В этом случае проблемной становится |
| 153 | +отладка программы, если в одной из объявленных переменных допущена ошибка |
| 154 | + |
| 155 | +```diff |
| 156 | +- var container = document.querySelector('.container'), |
| 157 | +- buttons = container.querySelectorAll('button'), |
| 158 | +- panels = container.querySelectorAll('.panel'); |
| 159 | + |
| 160 | ++ var container = document.querySelector('.container'); |
| 161 | ++ var buttons = container.querySelectorAll('button'); |
| 162 | ++ var panels = container.querySelectorAll('.panel'); |
| 163 | +``` |
| 164 | + |
| 165 | +### Функции объявляются как переменные |
| 166 | +Функции должны объявляться как переменные. Особенности «подвешивания» функций |
| 167 | +позволяют их использовать до того как они были объявлены. Зачастую функция |
| 168 | +объявляется в локальной области видимости и в этом случае ее объявление |
| 169 | +не должно затрагивать всю область видимости. |
| 170 | + |
| 171 | +Положительный побочный эффект такого стиля заключается в том, |
| 172 | +что при использовании его в обучающем процессе идея коллбэков объясняется |
| 173 | +сильно проще |
| 174 | + |
| 175 | +```diff |
| 176 | +- function onAnimationEnd(callback) { |
| 177 | +- if (isExecuting) { |
| 178 | +- function callbackToQueue() { |
| 179 | +- callback(); |
| 180 | +- queue.splice(queue.indexOf(callbackToQueue), 1); |
| 181 | +- } |
| 182 | +- |
| 183 | +- queue.push(callbackToQueue); |
| 184 | +- } else { |
| 185 | +- callback(); |
| 186 | +- } |
| 187 | +- } |
| 188 | + |
| 189 | ++ var onAnimationEnd = function(callback) { |
| 190 | ++ if (isExecuting) { |
| 191 | ++ var callbackToQueue = function() { // Совсем хорошо, если let |
| 192 | ++ callback(); |
| 193 | ++ queue.splice(queue.indexOf(callbackToQueue), 1); |
| 194 | ++ } |
| 195 | ++ |
| 196 | ++ queue.push(callbackToQueue); |
| 197 | ++ } else { |
| 198 | ++ callback(); |
| 199 | ++ } |
| 200 | ++ } |
| 201 | +``` |
| 202 | + |
| 203 | +### Замыкания |
| 204 | +```diff |
| 205 | +- function onItemClick(item, i) { |
| 206 | +- item.onClick = function() { |
| 207 | +- var clickedItem = i; |
| 208 | +- }; |
| 209 | +- } |
| 210 | + |
| 211 | ++ function onItemClick(item, i) { |
| 212 | ++ item.click = getClickHandler(i); |
| 213 | ++ } |
| 214 | ++ |
| 215 | ++ function getClickHandler(i) { |
| 216 | ++ var clickedItem = i; |
| 217 | ++ } |
| 218 | +``` |
0 commit comments