JavaScript styleguide for my projects
Switch branches/tags
Nothing to show
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Failed to load latest commit information.
README.md

README.md

Styleguide для JavaScript'а

Disclaimer

Это мой личный styleguide, продиктованный моим личным чувством прекрасного (и ужасного) и зоркостью моих глаз (точнее, отсутствием оной).

Основное положение

Экономить байты в коде должен обфускатор, а код должен быть максимально читаемым и понятным.

Читаемость

В частности, я не экономлю пустые строки и пробелы, отделяю все сколько-нибудь отдельные блоки кода пустыми строками, части выражений — пробелами.

Еще один важный момент: diff-friendly. Т.е. нужно минимизировать количество "мусорных" diff'ов.

Например, был такой кусок кода:

var a = 1,
    b = 2;

Добавили строчку, стало так:

var a = 1,
    b = 2,
    c = 3;

Вот здесь образовался "мусорный" diff:

- b = 2;
+ b = 2,

Именно поэтому я стараюсь писать код так, чтобы добавление, удаление, перенос строчек кода, не приводило бы к изменениям других строчек кода. Каждая строчка — независимо от соседей.

Общие положения

  • Кодировка файлов — utf-8;

  • Переводы строк — unix style;

  • В конце файла — перевод строки;

  • Индент — 4 пробела;

  • Не забываем удалять "висящие" пробелы, т.е. те, после которых нет непробельных символов;

  • Всегда явно ставим ;, не полагаясь на automatic semicolon insertion.

Блоки кода {...}

Открывающая { расположена на той же строке, что и оператор, к которому она относится.

//  Плохо:
if ( ... )
{
    ...
}

//  Хорошо:
if ( ... ) {
    ...
}

Закрывающая } выровнена по открывающему оператору (внутренняя по while, внешняя по if в данном случае):

if ( ... ) {
    while ( ... ) {
        ...
    }
}

Перед открывающей { всегда ставится пробел, а после — перевод строки. Однострочные блоки кода всегда запрещены:

//  Плохо.
while ( i < 10 ) { do_foo( i ); }

//  Хорошо.
while ( i < 10 ) {
    do_foo( i );
}

Везде, где синтаксис JavaScript'а разрешает использовать как блок кода { ... }, так и одиночное выражение, везде нужно использовать полноценный блок, причем, не однострочный:

//  Плохо.
if ( ... ) return;
if ( ... ) { return; }

//  Хорошо.
if ( ... ) {
    return;
}

Оператор if

  • Между if и ( — пробел.
  • Между ) и { — пробел.
  • Выражение внутри ( ... ) отбито пробелами.
//  Плохо.
if(i > 0){
    ...
}

//  Хорошо.
if ( i > 0 ) {
    ...
}

Тело if всегда в фигурных скобках:

//  Плохо.
if ( ... ) do_foo();
if ( ... ) return;

//  Хорошо.
if ( ... ) {
    do_foo();
}
if ( ... ) {
    return;
}

else на той же строке, что и } первого блока, перед else — пробел, перед { — пробел. Каждый блок, кроме последнего, заканчивается пустой строкой.

//  Плохо.
if ( ... ) {
    do_foo();
}
else {
    do_bar();
}

//  Хорошо.
if ( ... ) {
    do_foo();

} else {
    do_bar();
}

else if оформляется по тем же правилам, что и else:

if ( ... ) {
    do_foo();

} else if ( ... ) {
    do_bar();
}

if ( ... ) {
    do_foo();

} else if ( ... ) {
    do_bar();

} else {
    do_quu();
}

Оператор while

  • Между while и ( — пробел.
  • Между ) и { — пробел.
  • Выражение в ( ... ) отбивается пробелами.
//  Плохо.
while(i < 10){
    do_foo(i);
}

//  Плохо.
while ( i < 10 ) { do_foo( i ); }

//  Хорошо.
while ( i < 10 ) {
    do_foo( i );
}

Цикл do

  • Между do и { — пробел.
  • Между } и while — пробел.
  • Между while и ( — пробел.
  • Выражение в ( ... ) отбивается пробелами.
//  Плохо.
do{
    do_foo(i++);
}while(i < 10);

//  Плохо.
do { do_foo(i++); } while ( i < 10 );

//  Хорошо.
do {
    do_foo( i++ );
} while ( i < 10 );

Вообще говоря, я предпочитаю не пользоваться этим циклом вообще. Мне больше нравится более явный цикл while:

while ( true ) {
    do_foo( i++ );

    if ( i >= 10 ) {
        break;
    }
}

Оператор for

  • Между for и ( — пробел.
  • Между ) и { — пробел.
  • Содержимое внутри ( ... ) отбивается пробелами.
  • После ; — пробел, а перед — нет пробела.
//  Плохо.
for(i = 0;i < 10;i++){
    do_foo(i);
}

//  Плохо.
for (i = 0; i < 10; i++) { do_foo(i); }

//  Хорошо.
for ( i = 0; i < 10; i++ ) {
    do_foo( i );
}

Если переменные i и l не используются нигде, кроме тела цикла, то определяем их внутри:

for ( var i = 0, l = items.length; i < l; i++ ) {
    var item = items[ i ];
    ...
}

//  Плохо.
//  Переменная `i` в этом месте не "определена".
var j = i + 1;

Если же переменные цикла используются и за пределами блока, то определяем их отдельно:

var i, l;
for ( i = 0, l = items.length; i < l; i++ ) {
    ...
}

if ( i > 5 ) {
    ...
}

При этом нормально определять и использовать одни и те же переменные внутри нескольких циклов:

for ( var i = 0, l = items.length; i < l; i++ ) {
    ...
}

//  Да, формально i и l в JavaScript'е тут уже определены,
//  но мы притворяемся, что у нас блочный scope.
for ( var i = 0, l = items.length; i < l; i++ ) {
    ...
}

Оператор switch

  • Между switch и ( — пробел.
  • Между ) и { — пробел.
  • Условие в ( ... ) отбивается пробелами.
  • После { — перевод строки.
  • После case ...: — перевод строки.
  • Каждый case индентится, код для этого case еще индентится.
  • Между case-ами (или их группами) пустая строка.
  • Для default такие же правила, как и для case.
  • break на отдельной строке.
//  Плохо.
switch(id){
    case 'first': do_fooOne(); break;
    case 'second':
    case 'third': do_fooTwo();
        break;
    default: do_fooThree();
}

//  Хорошо.
switch ( id ) {
    case 'first':
        do_fooOne();
        break;

    case 'second':
    case 'third':
        do_fooTwo();
        break;

    default:
        do_fooThree();
}

Определение функции

Именованная функция

  • Между function и именем функции — пробел (очевидно).
  • Между именем функции и ( — нет пробела.
  • Между ) и { — пробел.
  • Если функция без аргументов, то пробелов между () нет.
  • Если аргументы есть, то они отбиваются пробелами с обеих сторон.
  • После запятой — пробел, перед запятой пробела никогда нет.
function foo() {
    ...
}

function bar( x, y ) {
    ...
}

Анонимная функция

  • В случае анонимной функции, ключевое слово function в каком-то смысле выполняет роль имени, поэтому между function и ( нет пробела:
function() {
    ...
}

function( x, y ) {
    ...
}

Определение переменных

Каждая переменная заслуживает своего личного var:

//  Плохо.
var i = 0,
    result = {},
    list = [];

//  Хорошо.
var i = 0;
var result = {};
var list = [];

Единственное исключение: набор однородных и связанных (т.е. эти переменные для одного и того же и не могут использоваться порознь) переменных, без инициализации. Например:

var i, j, k;
for ( i = 0; i < 10; i++ ) {
    for ( j = 0; j < 10; j++ ) {
        for ( k = 0; k < 10; k++ ) {
            ...

var i, l;
for ( i = 0, l = items.length; i < l; i++ ) {
    ...
}

То есть более-менее это переменные для итерации.

Если переменные не связаны (даже если нет инициализации), нужно использовать отдельные var:

var id;
var is_foo;

Не нужно выравнивать значения переменных в столбик:

//  Плохо.
var id =            42;
var title =         'Hello';
var is_something =  true;

//  Плохо.
var id              = 42;
var title           = 'Hello';
var is_something    = true;

//  Хорошо.
var id = 42;
var title = 'Hello';
var is_something = true;

Переменные определяются по месту использования, а не вначале scope'а. При этом считаем, что у нас блочный scope (на самом деле в JavaScript это не так).

//  Плохо.
if (...) {
    var i = 10;
    ...

}

//  Ошибка!
//  Запрещается использовать переменную вне блока,
//  в котором она определена.
var j = i + 1;


//  Хорошо.
var i;
if (...) {
    i = 10;
    ...
}

var j = i + 1;

Определение "классов"

//  Конструктор.
function Foo() {
    ...
}

//  Или же:
var Foo = function() {
    ...
};

//  Или же
ns.Foo = function() {

};

//  Наследуемся, если нужно.
no.inherit( Foo, Bar );

//  Каждый метод/свойство определяется отдельно.

Foo.prototype.id = 0;

Foo.prototype.foo = function() {
    ...
};

Именование сущностей

Константы

Используем ALL_CAPS стиль:

var PI = 3.1415;
var MAX_ID = 1000;

//  В этом случае `no.View` это неймспейс,
//  а `STATUS.OK` — название константы.
//
no.View.STATUS.OK = 'ok';

Конструкторы

Название конструктора всегда начинается с заглавной буквы. Используем Camel_Case:

function View() {
    ...
}

function View_Collection() {
    ...
}

//  Как вариант, если есть возможность:
View.Collection = function() {
    ...
};

Приватные свойства и методы объекта

Используем префикс _:

function Foo( data ) {
    this._data = data;
}

Foo.prototype._get_data = function() {
    return this._data;
};

Переменные

Используем under_score_style:

var model_id = model.id;

function get_model_id( model ) {
    return model.id;
}

Методы

Используем under_score_style:

model.get_data()

this.build_html_tree()

jQuery-объекты

Используем префикс $ для jquery-объектов:

var $node = $( node );

Это позволяет нам избегать ошибок типа:

$( $node ).find( ... )

Комментарии

Всегда используем инлайновые комментарии, а не блочные. Причина простая: код, в котором есть блочные комментарии, невозможно легко и просто закомментировать одним куском. А инлайновые комментарии этому не мешают.

Комментарии всегда на отдельной строке.

var i = 0; // Вот так ПЛОХО.

//  Хорошо.
//  Определяем переменную `i`.
var i = 0;

После // добавляем два пробела. Причина: комментарии в этом случае выравниваются "по сетке" В комментариях используем markdown. Когда-нибудь будем использовать тулзу для генерации из кода читаемой документации.

//  Комментарий, относящийся к блоку кода.
//  Чтобы комментарий (особенно многострочный) не прилипал впритык к коду,
//  рекомендуется добавлять пустую строку-комментарий.
//
function foo() {
    //  Поясняем конкретную строку.
    if ( x > 0 ) {
        ...
    }

    //  Коммент не привязанный к конкретному блоку кода.
    //  Отбивается сверху и снизу пустой строкой.

    //  Коммент про блок кода.
    //  Многострочный и многословный.
    //
    //  Считаем, что комментарии написаны с использованием `markdown`:
    //
    //      for ( var i = 0; i < 10; i++ ) {
    //          ...
    //      }
    //
    for ( var i = 0; i < 10; i++ ) {
        ...
    }
}

Комментарий должен быть отдельным предложением (или несколькими предложениями) — т.е. начинаться с заглавной буквы, заканчиваться точкой (обычно):

//  Плохо.
//  тут мы делаем что-то такое

//  Хорошо.
//  Тут мы делаем что-то такое.

Блочные комментарии всегда располагаем на отдельной строке.

//  Плохо.
/* function foo() {
    ...
} */

/*
//  Хорошо.
function foo() {
    ...
}
*/

И лучше не использовать их вообще. Разве что чтобы временно закомментить кусок кода.

jsdocs

Еще приходится использовать блочные комментарии для jsdocs. В целом же я противник jsdocs — не рассматриваю их как комментарии. Это какие-то инструкции для роботов (для IDE и прочих автоматических штук), а не для людей. Читать их невозможно, формат у них жуткий и громоздкий, и они только загромождают код и отвлекают от него.

Я не использую никаких IDE или других инструментов, умеющих извлекать пользу из jsdocs, поэтому: никаких jsdocs в моем коде! :)

Присваивания

  • = отбивается с двух сторон пробелами.
  • Каждое присваивание должно располагаться на отдельной строке:
//  Плохо.
s='Hello'; i=0;

//  Хорошо.
s = 'Hello';
i = 0;

Присваивание выражения со сравнениями

Используем скобки, чтоб явно отделить выражение от присваивания:

//  Плохо.
var a = b > 0;
var c = d === e;
var x = y > 0 && z < 0;

//  Хорошо.
var a = ( b > 0 );
var c = ( d === e );
var x = ( y > 0 && z < 0 );

При этом можно писать и так:

var is_foo = foo && bar;

и так:

var is_foo = ( foo && bar );

Присваивание в выражении

Не очень хорошая идея, но иногда бывает удобно использовать результат присваивания в качестве выражения. В этом случае присваивание оборачивается в (( ... )). Это говорит нам, что это не ошибка, что здесь не предполагается == или ===, а это явное присваивание:

//  Плохо.
if ( data = this.getData() ) {
    ...
}

//  Хорошо.
if (( data = this.getData() )) {
    ...
}

Выражения

Все бинарные операции (сравнения, арифметические и т.д.) всегда отбиваются пробелами с двух сторон:

if ( a == b || a > 1 ) {
    ...
}

Все унарные операции (унарный минус, инкременты, и т.д.) никогда не отбиваются пробелами:

if ( !a && -a < 3 || ~a ) {
    i++;
}

Скобки добавляем, если выражение сложное и приоритеты не читаются сразу. Но без фанатизма:

//  Плохо.
var a = b || c && d > 0 || b == 0;

//  Хорошо.
var a = ( b || ( c && ( d > 0 ) ) || ( b == 0 ) );

При сравнении выражения с константой, слева — выражение, справа — константа.

//  Плохо.
if ( 1 == a && true !== f( a ) ) {
    ...
}

//  Хорошо.
if ( a == 1 && f( a ) !== true ) {
    ...
}

Обращение к свойству объекта или элементу массива

Отбиваем содержимое [ ... ] пробелами с двух сторон:

array[ 0 ]
array[ i ]
array[ i + 1 ]
array[ index() ]

object[ 'foo' ]
object[ id ]
object[ 'id-' + id ]
object[ get_id() ]

== vs. ===

Всегда используем ===:

//  Плохо.
//  Даже несмотря на то, что результат `typeof` всегда `string`.
if ( typeof s == 'string' ) {

}

if ( typeof s === 'string' ) {
    ...
}

За исключением одного случая, когда нужно проверить, не является ли значение null или undefined.

if ( value == null ) {
    //  value === null || value === undefined.
    ...
}

typeof, instanceof, ...

Как правило не нужны скобки вокруг аргументов этих операторов:

//  Плохо.
typeof (x) === 'string'
(x) instanceof Array

//  Хорошо.
typeof x === 'string'
x instanceof Array

Тернарный оператор

Условие в скобках, вокруг ? и : — пробелы:

//  Плохо.
var x = a > 0? 1 : 0;

//  Хорошо.
var x = ( a > 0 ) ? 1 : 0;

Вложенные тернарные операторы запрещены.

//  Плохо.
var r = (a > 0) ? (b > 0) ? a : b : 0;

//  Хорошо.
var t = ( b > 0 ) ? a : b;
var r = ( a > 0 ) ? t : 0;

//  Хорошо.
var r;
if ( a > 0 ) {
    r = ( b > 0 ) ? a : b;

} else {
    r = 0;
}

//  Нормально.
//  Хоть и громоздко, но читаемо.
//
var r;
if ( a > 0 ) {
    if ( b > 0 ) {
        r = a;

    } else {
        r = b;
    }

} else {
    r = 0;
}

Пусть обфускатор с этим разбирается.

if vs. тернарный оператор, &&, ||

Да, JavaScript позволяет использовать тернарный оператор в качестве замены if:

//  Плохо.
(a > 0) ? f() : g();
(a > 0) && f();
(a > 0) || g();

Но это дурной тон. Никогда-никогда так не делаем.

Не ленимся, не экономим байты и используем if:

//  Хорошо.
if ( a > 0 ) {
    f();

} else {
    g();
}

//  Хорошо.
if ( a > 0 ) {
    f();
}

//  Хорошо.
if ( !( a > 0 ) ) {
    g();
}

Правило очень простое. Если блок не возвращает значение или же мы это значение игнорируем, то всегда используется if. Если у блока есть побочные эффекты, то всегда используем if.

Тернарный оператор, && и || могут использоваться как часть выражения, возвращаюшего значение и не имеющего side effects:

//  Хорошо.
a = b && c;
x = ( y > 0 ) ? z() : w();
foo = foo || 42;

Вызов функции

  • Между именем функции и ( нет пробела.
  • Если параметры не передаются, то между () пробелов нет.
  • Если параметры есть, то они отбиваются пробелами.
  • После запятой есть пробел, перед — нет.
//  Плохо.
foo( )
bar(x,y)
foo(bar(x), bar(y, quu(z)))

//  Хорошо.
foo()
bar( x, y )
foo( bar( x ), bar( y, quu( z ) ) )

Если в одной строчке слишком много вложенных вызовов, то можно это как-то переформатировать:

var t = bar( y, quu( z ) );
foo( bar( x ), t );

//  Или.
foo(
    bar( x ),
    bar( y, quu( z ) )
);

Object-литерал в качестве параметра:

foo( { foo: 42, bar: 24 } )
foo( x, { foo: 42, bar: 24 } )
foo( { foo: 42, bar: 24 }, x )

foo( {
    foo: 42,
    bar: 24
} )

foo( x, {
    foo: 42,
    bar: 24
} )

foo( {
    foo: 42,
    bar: 24
}, x )

foo(
    x,
    {
        foo: 42,
        bar: 24
    }
)

Array-литерал аналогично:

foo( [ 1, 2, 3 ] )
foo( [ 1, 2, 3 ], x )
foo( x, [ 1, 2, 3 ] )

foo( [
    1,
    2,
    3
] )

foo( x, [
    1,
    2,
    3
] )

foo( [
    1,
    2,
    3
], x )

foo(
    [ 1, 2, 3 ],
    x
)

foo(
    [
        1,
        2,
        3
    ],
    x
)

Функция в качестве параметра (аналогично):

foo( function() {
    ...
} )

this.foo( 'bar', function() {
    ...
}, true )

this.foo(
    'bar',
    function() {
        ...
    },
    true
)

Если в функцию передается несколько длинных выражений, то лучше каждое разместить на отдельной строке:

foo(
    first_long_argument,
    second_long_argument,
    third_long_argument
)

Если есть несколько многострочных параметров, то лучше каждый разместить на отдельной строке с индентом:

//  Плохо.
this.then( function() {
    ...
}, function() {
    ...
} )

//  Плохо.
foo( {
    foo: 42
}, {
    bar: 24
} )

//  Хорошо.
this.then(
    function() {
        ...
    },
    function() {
        ...
    }
)

//  Хорошо.
foo(
    {
        foo: 42
    },
    {
        bar: 24
    }
)

Chaining

Chain-вызовы в количестве больше двух быстро превращают код в нечитаемую кашу. Каждый вызов лучше расположить на отдельной строки, начинающейся с точки.

//  Плохо.
function quote(s) {
    return s.toString().replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}

//  Лучше, но все равно плохо.
function quote(s) {
    return s.toString().replace(/&/g, '&amp;')
        .replace(/</g, '&lt;').replace(/>/g, '&gt;');
}

//  Хорошо.
function quote( s ) {
    return s
        .toString()
        .replace( /&/g, '&amp;' )
        .replace( /</g, '&lt;' )
        .replace( />/g, '&gt;' );
}

Object literal

В однострочном объекте отбиваем содержимое от скобок пробелом. После двоеточия всегда пробел, перед — нет пробела.

//  Плохо.
{id : 42, title : 'Hello'}

//  Хорошо.
{ id: 42, title: 'Hello' }

Многострочный объект. Запятая в конце строки.

{
    id: 42,
    title: 'Hello'
}

Никогда не выравниваем значения в столбик:

//  Плохо.
{
    id:     42,
    title:  'Hello'
}

(Спорно) В случае, когда некоторые ключи нужно кавычить, лучше делать это для всех ключей. Даже для тех, для которых это не нужно:

//  Не очень хорошо.
{
    id: 42,
    'category-id': 'life',
    'post-title': 'Hello'
}

//  Лучше.
{
    'id': 42,
    'category-id': 'life',
    'post-title': 'Hello'
}

Array literal

В однострочном массиве отбиваем содержимое от скобок пробелом. После запятой всегда пробел, перед запятой нет пробела:

//  Плохо.
[1,2,3]

//  Хорошо.
[ 1, 2, 3 ]

Многострочный массив:

[
    1,
    2,
    3
]

В многострочном массиве каждое значение на отдельной строке:

//  Не очень хорошо.
[
    1, 2, 3,
    4,
    5
]

Но возможен вариант а-ля матрица:

[
    1, 2, 3,
    4, 5, 6,
    7, 8, 9
]

Не определять методы внутри object literal

//  Плохо.
var obj = {
    getData: function() {
        return this.data;
    },
    ...
};

//  Хорошо.
var obj = {};

obj.getData = function() {
    return this.data;
};

Тоже самое, при использовании разных фабрик объектов:

//  Плохо.
ns.View.define( 'message', {
    events: {
        'changed': function() {
            ...
        },
        ...
    },

    methods: {
        'onclick': function() {
            ...
        },
        ...
    }
} );


//  Хорошо.
var events = {};

events.changed = function() {
    ...
};

var methods = {};

methods.onclick = function() {
    ...
};

ns.View.define( 'message', {
    events: events,
    methods: methods
} );


//  Еще лучше.
var message = {};

message.events = {};

message.events.changed = function() {
    ...
};

message.methods = {};

message.methods.onclick = function() {
    ...
};

ns.View.define( 'message', message );

var that = this;

Стандартный "заменитель" для thisthat:

var that = this;
do_async( function() {
    that.do_foo();
} );

Immediately-Invoked Function Expression (IIFE)

Используется для явного создания замыканий и для обертки модулей:

( function() {
    ...
} )();

( function( x, y ) {
    ...
} )( foo(), bar() );

Обертка для модуля

Часто весь модуль заворачивается в IIFE, чтобы не засорять глобальный контекст:

//  Неймспейс.
var ns = {};

( function() {

//  ---------------------------------------------------------------------------------------------------------------  //

//  Глобальные переменные модуля,
//  которые не должны засорять общий глобальный контекст.
//
var id = 42;

ns.foo = function() {
    ...
}

//  ---------------------------------------------------------------------------------------------------------------  //

} )();

При этом содержимое модуля не индентится. Только в этом случае — когда это на самом верхнем уровне файла.

Вот здесь уже должен быть индент, т.к. здесь это не wrapper модуля, а просто обертка для замыкания:

var results = [];
for (var i = 0; i < 10; i++) {
    ( function(i) {
        //  Должен быть индент.
        get( function( result ) {
            results[ i ] = result;
        } );
    } )( i );
}

Шоткаты в модуле

Не используем шоткаты!

//  Плохо.

//  Глобальный неймспейс.
var ns = ns || {};

(function() {

    function Foo() {
        ...
    }

    Foo.prototype.foo = function() {
        ...
    };

    ns.Foo = Foo;

    //  Т.е.`Foo` это шоткат к `ns.Foo` внутри модуля.

})();

//  Плохо.

var ns = ns || {};

(function() {

    function Bar() {
        ...
    };

    Bar.prototype.bar = function() {

    };

    ns.foo = {};
    ns.foo.Bar = Bar;

    //  Т.е. `Bar` это шоткат к `ns.foo.Bar` внутри модуля.

})();

//  Плохо.

var ns = ns || {};

(function() {

    var bar = ns.foo.bar;

    bar.Bar = function() {
        ...
    };

    bar.Bar.prototype.quu = function() {
        ...
    };

    //  Т.е. `bar` это шоткат к `ns.foo.bar`.

})();

Всегда явно указывает полный путь определяемых функций, классов и т.д.:

//  Хорошо.

var ns = ns || {};

(function() {

    ns.Foo = function() {
        ...
    }

    ns.Foo.prototype.foo = function() {
        ...
    };

})();

//  Хорошо.

var ns = ns || {};

(function() {

    ns.foo = {};

    ns.foo.Bar = function() {
        ...
    };

    ns.foo.Bar.prototype.bar = function() {

    };

})();

//  Хорошо.

var ns = ns || {};

(function() {

    ns.foo.bar.Bar = function() {
        ...
    };

    ns.foo.bar.Bar.prototype.quu = function() {
        ...
    };

})();

Почему я против шоткатов? Они не позволяют быстро грепать код, не позволяют по одной строчке понять, что именно нашлось. Сравните (типа результат grep Bar):

function Bar() {

и ns.foo.Bar = function() {

В последнем варианте видно и неймспейс, и поднеймспейс, и класс и все, что нужно.

По тем же причинам я против определения методов внутри object literal. Там еще сильнее разница:

isValid: function() {

и ns.View.prototype.isValid = function() {

Кавычки

Всегда используем одинарные кавычки:

// Во-первых, удобнее набирать — не нужно нажимать шифт.
var msg = 'Hello, World!';

// Во-вторых, в html'ных (xml'ных, json'ных) фрагментах можно использовать двойные кавычки и не эскейпить их.
//
var html = '<div class="hello">Hello, World</div>';
var json = '{ "foo": 42, "bar": "Hello" }';

HRule

Для визуального разбиения кода на части используется вот такой разделитель:

function foo() {
    ...
}

//  ---------------------------------------------------------------------------------------------------------------  //

function bar() {
    ...
}

Его длина — 119 символов (не спрашивайте). Обратите внимание, по два пробела после и перед //.

Сверху и снизу отбивается пустой строкой.

Можно использовать для "заголовков", начинающих большую секцию кода:

//  ---------------------------------------------------------------------------------------------------------------  //
//  ns.View
//  ---------------------------------------------------------------------------------------------------------------  //

ns.View = function() {
    ...
};

//  ---------------------------------------------------------------------------------------------------------------  //

ns.View.prototype.foo = function() {
    ...
};

ns.View.prototype.bar = function() {
    ...
};



//  ---------------------------------------------------------------------------------------------------------------  //

Внутри секции используется опять-таки разделитель, а перед последним стоит не одна пустая строка, а две-три, чтобы подчеркнуть, что секция тут заканчивается.

Не синтаксический styleguide

Пока что в процессе. Все, что выше — было про синтаксис, про пробелы, скобки, переводы строк и т.д.

Еще должно быть что-то про содержательную часть.

Где можно использовать this

  • В методах объектов, инстанцированных через new Foo(), где Foo это конструктор.

    Например, вот здесь нельзя использовать this:

    //  `o` не является инстансом какого-либо класса.
    var o = {};
    
    o.id = 42;
    
    o.get_id = function() {
        return this.id;
    };
    
  • Например, в случае, когда к объекту примиксован интерфейс подписки на события и обработчики событий вызываются в контексте этого объекта:

    view.on( 'init', function() {
        this.do_foo();
    } );
    
    view.trigger( 'init' );
    

    Тут важный, но сложный, момент. Эти методы должны вызываться со сменой контекста централизовано, специально написанным кодом. Например, метод trigger — такое место.

    Вот такой вариант не годится:

    function foo() {
        this.do_foo();
    }
    
    foo.call( view );
    

    Грубо говоря, call/apply могут быть в "библиотечном" коде и тогда в "юзерском" коде могут быть функции/методы, использующие this. Но в "юзерском" коде никаких call/apply быть не должно.

По возможности не использовать call и apply

См. предыдущий пункт.

Следует избегать принудительной смены контекста при вызове функций. Это сильно затрудняет понимание кода, т.к. из кода такой функции непонятно, а что же там будет в this:

function foo() {
    //  Что такое `this` здесь?
    return this.foo;
}

Вот тут понятно:

Foo.prototype.foo = function() {
    return this.foo;
};

Вот тут более-менее понятно:

ns.View.define('foo', {
    events: {
        'click': function() {
            this.do_foo();
        }
    }
});

Не использовать bind

Придерживаюсь мнения, что bind — это страшное зло.

См. предыдущие два пункта. Напишу больше потом.