Правила написания JavaScript кода - Gemorroj/noads-advanced GitHub Wiki
Правила написания JavaScript кода
Данный обзор предназначен только для личного использования. Это не сборник абсолютных/идеальных правил, если существующий код уже написан в определённом стиле, то именно ему стоит отдать предпочтение.
Подразумевается возможность развития и обновления этого документа если какие-то идеи окажутся неудачными.
TL:DR;
-
FullCamel именование пространств и конструкторов.
-
camelCase или mixed_underscore для названий переменных и функций.
-
UPPERCASE_UNDERSCORED для констант.
-
Предпочтительны понятные названия функций.
-
Отступ в 4 пробела.
-
Пробелы после двоеточий и запятых.
-
Использование паттерна "конструктор".
-
В случаях если параметры функции не ясны из её названия стоит использовать именованные параметры.
-
В замыканиях стоит использовать 'self' для хранения ссылки на конструируемый объект.
-
Стоит использовать исключения, а не тихое отключение.
Определение перемененных
Переменные должны определяться в верху функции, даже если они определяются
ниже по коду. Если переменной присваивается значение, эта запись должна идти отдельной
строкой кода; если переменные только объявляются можно писать их одной строкой, но
только в самом начале определения. Перед знаком =
и после него должны быть пробелы.
Переменные должны быть выровнены по левой кромке.
var yeah, this_is, a, way,
foo = 1,
bar = 2;
Предполагается использование camelCase
стиля именования методов и mixed_underscore
для атрибутов и сервисных функций. Подумайте как лучше выразить назначение функции или
переменной, скажем массивы лучше именовать во множественном числе, а перед булевыми
перемененными использовать is
.
Отступы и пробелы
Для отступа используются четыре пробела. Стоит убирать пробелы перед переносом строки. После двоеточий и запятых пробелы необходимы всегда.
Строки
Для определения строк используются одинарные кавычки.
Блоки
Из-за того, что используется явное указание точек-с-запятой всегда начинайте фигурную скобку на той строке, где идёт открывающее выражение. Например:
if (foo) {
bar();
}
if (foo) {
foo = 1;
return false;
}
Комментирование
В целом рекомендуется комментировать сложные моменты и игнорировать очевидный код. Не стоит добавлять бессмысленные, личные или расплывчатые комментарии.
Если оказывается что необходимо постоянно что-то комментировать, подумайте над тем не стоит ли разбить код на именованные функции, описывающие его работу.
При работе с откомментированным кодом не забывайте проверять сохраняется ли актуальность данного комментария и исправлять/удалять его.
Для длинных строк нужно использовать /* ... */
.
Длинные одно-строчные комментарии должны быть отдельной строчкой, предпочтительно над комментируемым кодом. Так же перед ними стоит оставлять пустую строку.
var some = "stuff";
// Делаем цикл
for (var i = 0; i < 10; i++ ) {
doIt();
}
Комментарии внутри строки можно использовать только для описания специфических аргументов в списке формальных параметров:
function foo(types, selector, data, fn, /*ВНУТРЕННИЙ*/ one ) {
// do stuff.
}
Массивы и инициализация объектов
Пустые объекты и массивы не должны содержать пробелов. Однострочные массивы и инициализаторы объектов допустимы только если они умещаются в строку-две.
var arr = [1, 2, 3]; // Без пробелов после [ или до ].
var obj = {a: 1, b: 2, c: 3}; // Без пробелов после { или до }.
Отступы многострочных инициализаторов аналогичны таковым у блоков.
var inset = {
top: 10,
right: 20,
bottom: 15,
left: 12
};
Обратите внимание на то, что перед двоеточием пробела нет.
this.rows = [
'"Чёртпобериэтоттекст" ',
'"Marvin the Paranoid Android" ',
'[email protected]'
];
Функции
По возможности избегайте анонимных функций. По определению если функции не присвоено имя, она считается анонимной: неудачно:
this.do_the_magic = function () {
//...
}
Хотя она и назначена осмысленному атрибуту, функция была задана анонимной.
удачно:
this.do_the_magic = function do_the_magic() {
//...
}
Приложение сделанное из множества анонимных функций почти невозможно профилировать, а трасса вызовов функции оказывается бессмысленной.
Незначительные по размеру блоки, вроде функций обратного вызова или обработчиков событий могут и не иметь подходящего имени, в таких случаях анонимность оправданна.
Об именовании
Правильное именование функций очень важно. Внятные имена функций могут помочь прочесть код. Не забывайте о ясности. Длинные имена не представляют никаких проблем.
Вызов функций
Вызов функции должен содержать пробелы между параметрами, но не должен содержать пробелов около открывающей и закрывающей скобок:
foo('bar', 1);
Простые вызовы должны быть в одну строку:
activate(minigun, '10000+rounds');
Если функция вызывается со множеством параметров или длинными аргументами, её вызов должен разделяться на название и список аргументов, по одному на строку:
activate(
the_super_multi_laser_array,
'Piiiiiuuuuuuuuuuuuuuuuuuuuuuuuuuu'
);
К слову, если аргументы содержат объекты с длинными атрибутами, их стоит тоже записать в несколько строк:
activate(
the_super_multi_laser_array,
settings: {
voltage: Piiiiiuuuuuuuuuuuuuuuuuuuuuuuuuuu,
target: "WTF are you targeting our satellites XD"
}
);
Конструкторы
Существует несколько способов создания объектов. В целом рекомендуется использовать паттерн "конструктор":
function Laser() {
this.fire = function fire() {
//...
}
}
window.the_laser = new Laser();
Вложенные конструкторы допустимы, но перед этим стоит подумать "а стоит ли".
function Laser() {
var power_supply = new PowerSupply();
this.fire = function fire() {
power_supply.turn_on();
//...
}
function PowerSupply() {
//...
}
}
Self
Часто требуется сохранить ссылку на конструируемый объект для использования
во внутренних функциях. Для этого стоит задать переменную self
:
function Laser() {
var self = this;
function fire() {
set_voltage(self.voltage);
}
}
Стоит отметить что эта строка всегда идёт в конструкторе первой.
Использование именованных параметров
Использование именованных параметров уменьшает повторения(?) и делает код читабельнее. Их использование особенно полезно если из названия функции не ясно что в неё передавать.
Неудачно:
function do_the_thing(tempo, start_at_step, repeats) {
//...
}
do_the_thing(120, 5, 3);
Чтобы в таком коде понять что делает вызов функции, придётся возвращаться к её определению.
Удачно:
function do_the_thing(params) {
//params: tempo, start_at_step, repeats
}
do_the_thing({
tempo: 120,
start_at_step: 5,
repeats: 3
});
или же:
function do_the_thing(params) {
var tempo = params.tempo;
var start_at_step = params.start_at_step;
var repeats = params.repeats;
}
do_the_thing({
tempo: 120,
start_at_step: 5,
repeats: 3
});
В данном примере становится очевидно что представляет собой каждый параметр.
Что нужно для начала работы функции должно быть очевидно в самом её начале. При её определении либо заносите в var все используемые перемененные, либо комментируйте что из параметров будет использоваться и для чего.
Вложенные функции
Вложенные функции неплохо подходят для разбиения функционала и могут использоваться свободно. Если функция используется только другой функцией её стоит определить внутри последней.
Неудачно:
function foo_plus_bar() {
return get_foo_for_foo_plus_bar() + 'bar';
};
function get_foo_for_foo_plus_bar() {
return 'foo';
};
Удачно:
function foo_plus_bar() {
function get_foo() {
return 'foo';
};
return get_foo() + 'bar';
};
Как исключение иногда стоит определять дочернюю функцию вне основной с целью избегания перекрёстных ссылок (см. раздел о замыканиях).
Модульная структура
Общее поведение может быть сгруппировано в отдельные модули:
function PoweredThing() {
this.set_voltage = function set_voltage() {
//...
}
}
function Laser() {
PoweredThing.apply(this);
}
var laser = new Laser();
laser.set_voltage(100000000);
Файлы
В целом рекомендуется создавать по файлу на каждый конструктор или логический блок. Названия должны быть строчными и описывать назначение кода.
Код в представлении
Допустимо использование встроенного JavaScript-кода, но только если инициализаторы связаны с содержимым страницы. В целом код должен быть отделён от представления.
Допустимо встраивать в представление установку констант.
Использование JavaScript в атрибутах HTML противопоказано:
<a onclick="do_something();">Запускаемся</a>
Применяйте ненавязчивый javascript с целью разделить функциональность (слой обработки) от представлений и кода страничек.
Языковые правила
Var
Всегда используйте var
. Никогда не применяйте неявного приравнивания. Скажем,
если хотите создать объект внутри window, так и пишите:
Неверно:
foo = 'bar';
Верно:
window.foo = 'bar';
Константы
Константы должны определяться аналогично обычным переменным, но при этом следовать своему стилю именования. Ключевое слово 'const' плохо поддерживается и его следует избегать.
Точки-с-запятой
Никогда не надейтесь на встроенное добавление точек-с-запятой. Они необходимы после каждого приравнивания или вызова функции. После блоков они не нужны.
Так же рекомендуют использовать точки-с-запятой после определений функций, хотя это и не обязательно.
После блоков for
/ while
/ switch
/ if
/ else
точки-с-запятой не используются
в целях повышения удобочитаемости кода.
var foo = 'bar';
// приравнивание: ';' нужна
function foo() {
alert("don't you foo me");
// вызов функции: ';' нужна
};
// ';' не нужна, но рекомендуется
this.foo = function foo() {
alert("а ну не фукай");
};
// ';' нужна, т.к. приравнивание
if (foo) {
foo();
}
// ';' здесь не нужна
Тернарный оператор
Тернарный оператор полезен при назначении переменных. Однако вложенные тернарные операторы недопустимы из-за их низкой читаемости.
Неудачно:
var a = b ? c : d ? e : f;
Проверка на равенство
В большинстве случаев необходимо использование строгой проверки на (не)равенство
(===
and !==
). В частности желательно приведение перемененных перед сравнением.
Проверки типов
Проверка типов по возможности должна делаться с помощью именованных методов:
arr.isArray
, str.isString
, func.isFunction
, и obj.isObject
.
Определение внутри блоков
Как общую практику избегайте объявлений внутри вложенных блоков. Особенно старайтесь избегать определения внутри блоков функций, т.к. это нестандартно для ECMAScript. ECMAScript допускает только определения функций в корневом блоке скрипта или функции(?).
Ужасно:
if (foo_is_wibble) {
function foo() { return 'wibble'; }
}
else {
function foo() { return 'bar'; }
}
foo();
Нормально:
function wibble() {
return 'wibble';
}
function bar() {
return 'bar';
}
var foo;
if (foo_is_wibble) {
foo = wibble;
}
else {
foo = bar;
}
foo();
Как общее правило переменные стоит выносить из блоков.
Неудачно:
for (var i=0; i<5 i++) {
var wibble = new Wibble(i);
wibbles.push(bar);
}
Удачно:
var wibble;
for (var i=0; i<5 i++) {
wibble = new Wibble(i);
wibbles.push(wibble);
}
Исключения
Используйте исключения. Если нужно обработать ошибки генерируйте throw. Единственно,
не стоит злоупотреблять блоками try
/ catch
, т.к. они не слишком быстро
обрабатываются.
Не бойтесь создать своё исключение, т.к. если что-то сломалось - оно сломалось и нам нужно это знать, чтобы сэкономить время при отладке. Никогда не делайте "тихих" сбоев.
Создание примитивов
Не используйте обёртки примитивов, а задавайте их явно:
var str = 'foo';
var bool = false;
var arr = [];
var num = 10;
var obj = {};
В частности не нужно использовать new
с конструкторами примитивов, вот почему:
typeof(Boolean()) // ==> 'boolean'
typeof(new Boolean()) // ==> 'object'
В JavaScript есть пять типов примитивов: undefined
, null
, boolean
, number
, и string
.
Замыкания
Замыкания великолепны. Порой они захватывают дух, однако используя их стоит быть внимательным, особенно если код может работать длительное время.
Рассмотрим пример:
function foo(element, a, b) {
element.onclick = function handle_click() { /* использует a и b */ };
}
В данном примере создана перекрёстная ссылка, вызывающая утечку памяти.
Функция handle_click
содержит element
в своей области видимости и element
содержит
handle_click
, что означает, что ни одна не может быть удалена.
function foo(element, a, b) {
element.onclick = bar(a, b);
}
function bar(a, b) {
return function() { /* использует a и b */ }
}
В данном случае создано второе замыкание не содержащее element
и исключающее перекрёстную ссылку.
Eval
Основное назначение eval
для десериализации. Если вы используете его для чего-то ещё,
вы увлеклись.
With
В целом - не стоит. Использование with
легко маскирует область видимости
и даёт нечитаемый код.
This
Использование this
может сбивать с толку. В целом не стоит использовать его кроме как в
конструкторах или совместно с call
или apply
.
Использование конечных элементов DOM в вызовах событий говорит о непродуманном коде и как бы намекает на необходимость другого подхода.
Циклы for … in …
Не стоит использовать этот метод при обработке массивов.
Модификация прототипов встроенных объектов
К примеру:
Array.prototype.find = function () {....}
Встроенные конструкторы не должны модифицироваться! Если нужно расширить функционал встроенных классов создайте новый конструктор, который создаёт объект и добавляет к нему свой функционал.