JavaScript Style Guide

Руководство по стилю от Google, содержащее общие правила о том, как писать код. Все правила синтаксиса в данном посте - это рекомендации по улучшению читаемости вашего кода. О любых можно поспорить.

Имя файла

Имена файлов должны быть в нижнем регистре и могут включать нижнее подчеркивание (_) или "тире" (-), но без любой другой пунктуации. Придерживайтесь договоренностей, которые используются в вашем проекте.

Кодировка файлов должна быть в UTF-8.

Форматирование

Скобки

Скобки требуются для всех управляющих структур (ifelsefordowhile и т.п.), даже если тело содержит одну инструкцию. Первая инструкция непустого блока должна начинаться с отдельной строки.

Скобки следуют стилю "египетские скобки" для непустых блоков и блочных конструкций:

  • Никакого разрыва строки перед открывающей скобкой.
  • Разрыв строки после открывающей скобки.
  • Разрыв строки перед закрывающей скобкой.
  • Разрыв строки после закрывающей скобки if. Данная фигурная скобка завершает инструкцию, тело функции, класс или метод класса. В частности, после фигурной скобки нет разрыва строки, если за ней следует elsecatchwhile или запятая, точка с запятой или правая скобка.
// Египетские скобки
if (a === b) {
  console.log("hello");
}

Пустой блок или блочно-подобная конструкция может быть закрыта сразу после открытия, но без символов, пробелов или разрыва строки между ними (например, {}), Если только это не является частью нескольких блоков инструкций (который содержит непосредственно несколько блоков: if/else или try/catch/finally).

Пример:

function doNothing() {}

Запрещено:

if (condition) {
  // …
} else if (otherCondition) {} else {
  // …
}

try {
  // …
} catch (e) {}

Блочный отступ: +2 пробела

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

Литералы массива: опционально "блочные"

Любой литерал массива может быть необязательно отформатирован так, как если бы он был «блочной конструкцией». Например, допустимые варианты ниже (не исчерпывающий список):

const a = [
  0,
  1,
  2,
];

const b =
    [0, 1, 2];
const c = [0, 1, 2];

someMethod(foo, [
  0, 1, 2,
], bar);

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

Объектные литералы: опционально "блочные"

Любой объектный литерал может быть отформатирован так, как если бы он был «блочной конструкцией». Применяются те же примеры, что и Литералы массива: опционально блочные. Например, следующие примеры допустимы (не исчерпывающий список):

const a = {
  a: 0,
  b: 1,
};

const b =
    {a: 0, b: 1};
const c = {a: 0, b: 1};

someMethod(foo, {
  a: 0, b: 1,
}, bar);

Литералы класса

Литералы класса (будь то объявления или выражения) имеют отступ в виде блоков. Не добавляйте точку с запятой после методов или после закрывающей скобки объявления класса (операторы, такие как присваивания, которые содержат выражения класса, по-прежнему заканчиваются точкой с запятой). Используйте ключевое слово extends, но не аннотацию @extends JSDoc, если только класс не расширяет шаблонизированный тип.

Пример:

class Foo {
  constructor() {
    /** @type {number} */
    this.x = 42;
  }

  /** @return {number} */
  method() {
    return this.x;
  }
}
Foo.Empty = class {};
/** @extends {Foo<string>} */
foo.Bar = class extends Foo {
  /** @override */
  method() {
    return super.method() / 2;
  }
};

/** @interface */
class Frobnicator {
  /** @param {string} message */
  frobnicate(message) {}
}

Функциональные выражения

При объявлении анонимной функции в списке аргументов для вызова функции, тело функции имеет отступ на два пробела больше, чем предыдущая глубина отступа.

Пример:

prefix.something.reallyLongFunctionName('whatever', (a1, a2) => {
  // Отступ тела функции +2 относительно глубины отступа
  // оператора на одну строку выше.
  if (a1.equals(a2)) {
    someOtherLongFunctionName(a1);
  } else {
    andNowForSomethingCompletelyDifferent(a2.parrot);
  }
});

some.reallyLongFunctionCall(arg1, arg2, arg3)
    .thatsWrapped()
    .then((result) => {
      // Отступ тела функции +2 относительно глубины отступа
      // вызова '.then()'.
      if (result) {
        result.use();
      }
    });

Switch операторы

Как и в любом другом блоке, содержимое блока switch имеет отступ +2.

После блока switch добавляется новая строка и уровень отступа увеличивается на +2, точно так же, как если бы блок открывался. Явный блок может использоваться, если этого требует лексическая область видимости. После блока кода отступ возвращается на предыдущий уровень.

Пустая строка необязательна между break и следующим случаем.

Пример:

switch (animal) {
  case Animal.BANDERSNATCH:
    handleBandersnatch();
    break;

  case Animal.JABBERWOCK:
    handleJabberwock();
    break;

  default:
    throw new Error('Неизвестное животное');
}

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

Внутри блока switch каждый блок case прерывается (с помощью breakreturn или throw), или помечается комментарием, указывающим на то, что выполнение будет или может быть продолжено в следующем блоке case. Достаточно любого комментария, который передает идею продолжения (обычно пишется // fall through). Этот специальный комментарий не требуется в последнему блоку инструкций в блоке switch.

Пример:

switch (input) {
  case 1:
  case 2:
    prepareOneOrTwo();
    // fall through (продолжение)
  case 3:
    handleOneTwoOrThree();
    break;
  default:
    handleLargeNumber(input);
}

Блок default должен присутствовать

Каждый оператор switch включает оператор default, даже если он не содержит кода. Оператор default должен быть последним.

this

Используйте this только в конструкторах и методах класса, в стрелочных функциях, определенных внутри конструкторов и методов класса, или в функциях, которые имеют явный @this, объявленный в JSDoc, а также в функциях, которые сразу закрываются.

Никогда не используйте this для обращения к глобальному объекту, контексту evaltarget принадлежащему event, или ненужному call() или apply() функций.

Проверка равенства

Используйте операторы сравнения (===/!==), за исключением случаев, описанных ниже.

Исключения, где необходимо приведение типов

Поимка с помощью catch обоих значений null и undefined:

if (someObjectOrPrimitive == null) {
  // Проверка на наличие null ловит и null, и undefined для объектов и
  // примитивов, но не ловит другие неправильные значения, как 0 или пустые
  // строки.
}

Именование

Общие для всех идентификаторов правила

Идентификаторы используют только ASCII-буквы и цифры, и в небольшом количестве случаев, отмеченных ниже, подчеркивания и очень редко (когда этого требуют такие фреймворки, как Angular) знаки доллара.

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

errorCount          // Без аббревиатуры.
dnsConnectionIndex  // Большинство людей знает, что такое "DNS".
referrerUrl         // То же самое для "URL".
customerId          // "Id" используется везде, поэтому вряд ли будет неправильно понят.

Запрещено:

n                   // Бессмысленный.
nErr                // Двусмысленная аббревиатура.
nCompConns          // Двусмысленная аббревиатура.
wgcConnections      // Только ваша команда знает, что это значит.
pcReader            // Многие вещи можно назвать как "pc".
cstmrId             // Удалены внутренние символы.
kSecondsPerDay      // Не используйте венгерскую нотацию.

Правила для типов идентификатора

Именование

Имена должны быть в lowerCamelCase. Например, my.exampleCode.deepSpace, но не my.examplecode.deepspace или my.example_code.deep_space

Именование констант

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

Определение “constant”

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

Примеры:

// Константы
const NUMBER = 5;
/** @const */ exports.NAMES = ImmutableList.of('Ed', 'Ann');
/** @enum */ exports.SomeEnum = { ENUM_CONSTANT: 'value' };

// Не константы
let letVariable = 'non-const';
class MyClass { constructor() { /** @const {string} */ this.nonStatic = 'non-static'; } };
/** @type {string} */ MyClass.staticButMutable = 'не @const, может быть переопределено';
const /** Set<string> */ mutableCollection = new Set();
const /** ImmutableSet<SomeMutableType> */ mutableElements = ImmutableSet.of(mutable);
const Foo = goog.require('my.Foo');  // зеркально импортируемое имя
const logger = log.getLogger('loggers.are.not.immutable');

Имена констант обычно представляют собой существительные или существительные фразы.

Именование методов

Имена методов записываются в lowerCamelCase. Имена методов @private должны заканчиваться завершающим подчеркиванием.

Имена методов обычно представляют собой глаголы или глагольные фразы. Например, sendMessage или stop_. Методы getter и setter для свойств никогда не требуются, но если они используются, то они должны быть названы getFoo (или опционально isFoo или hasFoo для booleans) или setFoo(value) для setters.

Подчёркивания могут также появляться в именах методов тестирования JsUnit для разделения логических компонентов имени. Одним из типичных паттернов является test<MethodUnderTest>_<state>_<expectedOutcome>, например testPop_emptyStack_throws. Нет единого правильного способа называть методы для тестирования.

Имена классов

Имена классов, интерфейсов, записей и typedef'ов записываются в UpperCamelCase. Неэкспортируемые классы являются просто локальными: они не помечаются как @private и не заканчиваются завершающим подчеркиванием.

Имена типов обычно представляют собой существительные или фразы существительных. Например, RequestImmutableList или VisibilityMode. Кроме того, имена интерфейсов иногда могут быть прилагательными или прилагательными фразами (например, Readable).

Локальные псевдонимы

Локальные псевдонимы следует использовать всякий раз, когда они улучшают читабельность по сравнению с полноразмерными именами. Сохраняйте последнюю часть псевдонима. Псевдонимы также могут использоваться внутри функций. Псевдонимы должны быть const.

Примеры:

const staticHelper = importedNamespace.staticHelper;
const CONSTANT_NAME = ImportedClass.CONSTANT_NAME;
const {assert, assertInstanceof} = asserts;

Неконсатные названия полей

Неконстантные имена полей (статические или иные) записываются в lowerCamelCase, с завершающимися подчеркиванием для приватных полей.

Эти имена обычно являются существительными или фразами существительных. Например, computedValues или index_

Имена параметров

Имена параметров записываются в lowerCamelCase. Обратите внимание, что это применимо, даже если параметр ожидает конструктор.

Односимвольные имена параметров не должны использоваться в публичных методах.

Исключение: По требованию стороннего фреймворка имена параметров могут начинаться с $. Это исключение не распространяется на любые другие идентификаторы (например, локальные переменные или свойства).

Именование локальных переменных

Имена локальных переменных записываются в lowerCamelCase, за исключением модульно-локальных (верхнего уровня) констант, как описано выше. Имена констант в функциональных областях по-прежнему написаны в lowerCamelCase. Обратите внимание, что lowerCamelCase используется, даже если переменная содержит конструктор.

Имена параметров шаблона

Имена параметров шаблона должны быть краткими, однозначными или однобуквенными идентификаторами, и должны быть в ВЕРХНЕМ РЕГИСТРЕ, например TYPE или THIS.

Верблюжий стиль: отдельный случай

Иногда существует более одного разумного способа преобразования английской фразы в верблюжий стиль, например, когда присутствуют аббревиатуры или необычные конструкции вроде IPv6 или iOS. Для повышения предсказуемости Google Style задает следующую исчерпывающую (почти) схему.

Начиная с изначальной формы имени:

  1. Преобразуйте фразу в обычный ASCII и удалите все апострофы. Например,  может стать .

    Müller's algorithm

    Muellers algorithm

  2. Разделите этот результат на слова, разделяя их на пробелы и оставшиеся знаки препинания (обычно дефисы).

    1. Рекомендовано: если какое-либо слово уже имеет общепринятый внешний вид в camelCase, разделите его на составные части (например, AdWords становится "ad words"). Обратите внимание, что такое слово, как iOS, на самом деле, не совсем в camelCase; оно не поддается никаким соглашениям, поэтому данная рекомендация не применяется.
  3. Теперь сделайте все буквы в нижнем регистре (включая аббревиатуры), затем сделайте в верхнем регистре только первый символ:

    1. … каждого слова (чтобы вышел CamelCase в верхнем регистре) или
    2. … каждого слова, кроме первого (чтобы вышел camelCase в нижнем регистре)
  4. В конце, соедините все слова в один идентификатор.

Обратите внимание, что регистр исходных слов почти полностью игнорируется.

Примеры:

  • Приемлемо, но не рекомендуется.

Примечание: некоторые слова неоднозначны в английском языке: например, nonempty и non-empty оба правильны, поэтому имена методов checkNonempty и checkNonEmpty также правильны.

Инструкции

Одна инструкция на строку

После каждой инструкции делается перевод строки.

Необходима точка с запятой.

Каждая инструкция должна заканчиваться точкой с запятой. Использование автоматической точки с запятой запрещено.

Лимит строки: 80 символов

Код JavaScript имеет ограничение столбца в 80 символов.

Перенос строк

Основная директива переноса строк такова: предпочитайте переносить на более высоком синтаксическом уровне.

Предпочтительно:

currentEstimate =
    calc(currentEstimate + x * currentEstimate) /
        2.0;

Нежелательно:

currentEstimate = calc(currentEstimate + x *
    currentEstimate) / 2.0;

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

Операторы переносятся следующим образом:

  1. Когда у инструкции разрывается строка, разрыв следует после оператора. (Обратите внимание, что это не та же практика, что и в стиле Google для Java.)
    1. Это не относится к "точке" (.), которая на самом деле не является оператором.
  2. Имя метода или конструктора остается присоединенным к открытой круглой скобке ((), которая следует за ней.
  3. Запятая (,) остается прикрепленной к символу, который предшествует ей.

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

Вертикальный пробел

Одна пустая строка ставится:

  1. Между последовательными методами в литерале класса или объекта
    1. Исключение: пустая строка между двумя последовательными определениями свойств в литерале объекта (без другого кода между ними) является необязательной. Такие пустые строки используются по мере необходимости для создания логических группировок полей.
  2. Внутри тел методов имеет смысл создавать логические группы инструкций. Пустые строки в начале или конце тела функции не допускаются.
  3. Необязательно перед первым или после последнего метода в литерале класса или объекта (не рекомендуется и не поощряется).

Несколько последовательных пустых строк разрешены, но никогда не требуются (и не поощряются).

Горизонтальный пробел

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

Кроме случаев, когда этого требуют правила языка или другие правила стиля, а также литералы, комментарии и JSDoc, один ASCII пробел появляется только в следующих местах:

  1. Отделение любого зарезервированного слова (например, iffor или catch), за исключением function и super начиная с открытой скобки ((), которая следует за зарезервированным словом в этой же строке.
  2. Отделение любого зарезервированного слова (такого как else или catch) от закрывающей фигурной скобки (}), которая предшествует ему в этой строке.
  3. Перед любой открытой фигурной скобкой ({), с двумя исключениями:
    1. Перед литералом объекта, который является первым аргументом функции или первым элементом в литерале массива (например, foo ({a: [{c: d}]}) ).
    2. В расширении шаблона, так как это запрещено языком (например, валидно: ab${1 + 2}cd, не валидно: xy$ {3}z).
  4. С обеих сторон бинарного или тернарного оператора.
  5. После запятой (,) или точки с запятой (;). Обратите внимание, что пробелы никогда не разрешены перед этими символами.
  6. После двоеточия (:) в литерале объекта.
  7. По обе стороны от двойной косой черты (//), начинающей комментарий в конце строки. Здесь разрешено использование нескольких пробелов, но это не обязательно.
  8. После символа блочного комментария и с обеих сторон закрывающих символов (например, для кратких объявлений типов, приведений и комментариев имени параметра: this.foo = /** @type {number} */ (bar); или function(/** string */ foo) {; или baz(/* buzz= */ true)).

Аргументы функций

Предпочитайте помещать все аргументы функции в одну строку с именем функции. Если это превысит ограничение в 80 столбцов, аргументы должны быть перенесены для удобочитаемости. Чтобы сэкономить место, вы можете сделать перенос строки как можно ближе к 80 символам или поместить каждый аргумент в отдельной строке, чтобы улучшить читаемость. Отступы должны быть в размере 4-х пробелов. Выравнивание в скобках разрешено, но не рекомендуется. Ниже приведены наиболее распространенные шаблоны для переноса аргументов:

// Аргументы начинаются с новой строки, с отступом в четыре пробела. Предпочтительнее,
// когда аргументы помещаются не в одну строку с именем функции (или ключевым словом
// "function"), а помещаются полностью на второй строке. Такой подход работает с длинными
// именами функций, позволяет делать переименование без изменения отступов.
doSomething(
    descriptiveArgumentOne, descriptiveArgumentTwo, descriptiveArgumentThree) {
  // …
}

// Если список аргументов длинный (больше 80 символов), сделайте перенос.
// Такой подход использует меньше вертикального пространства, но нарушает
// правило прямоугольника и поэтому не рекомендуется.
doSomething(veryDescriptiveArgumentNumberOne, veryDescriptiveArgumentTwo,
    tableModelEventHandlerProxy, artichokeDescriptorAdapterIterator) {
  // …
}

// Четыре пробела, один аргумент в строке. Работает с длинными именами
// функций, позволяет переименование, и выделяет каждый аргумент.
doSomething(
    veryDescriptiveArgumentNumberOne,
    veryDescriptiveArgumentTwo,
    tableModelEventHandlerProxy,
    artichokeDescriptorAdapterIterator) {
  // …
}

Группировка скобок: рекомендуется

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

Не используйте лишние скобки вокруг всего выражения после deletetypeofvoidreturnthrowcaseinof или yield.

Круглые скобки необходимы для приведения типов: /** @тип {!Foo}. */(foo).

Стиль блочных комментариев

Блочные комментарии снабжены отступом на том же уровне, что и окружающий код. Они могут быть в стиле /* … */ или //. Для многострочных комментариев /* … */ последующие строки должны начинаться с *, выровненного с * на предыдущей строке, чтобы комментарии были очевидны без лишнего контекста.

/*
 * Это выглядит
 * хорошо.
 */

// Тоже
// хорошо.

/* Аналогично */

Комментарии не вставляются в поля, отрисованные звездочками или другими символами.

Комментарии для имен параметров

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

someFunction(obviousParam, /* shouldRender= */ true, /* name= */ 'hello');

Для согласованности с окружающим кодом можно поместить их после значения без =:

someFunction(obviousParam, true /* shouldRender */, 'hello' /* name */);

Объявление локальных переменных

Используйте const и let

Объявляйте все локальные переменные либо с помощью const, либо с помощью let. Используйте const по умолчанию, если только переменная не нуждается в переназначении. Ключевое слово var не должно использоваться.

Одна переменная для одного определения

Каждая декларация локальной переменной объявляет только одну переменную: декларации типа let a = 1, b = 2; не используются.

Определяйте, когда требуется, но инициализируйте — как можно быстрее

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

Строковые литералы

Используйте одиночные кавычки

Обычные строковые литералы разделяются одиночными кавычками ('), а не двойными кавычками (").

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

Обычные строковые литералы не могут охватывать несколько строк.

Шаблонные литералы

Используйте шаблонные литералы (разделенные ```) со сложной конкатенацией строк, особенно если речь идет о многострочных литералах. Шаблонные литералы могут занимать несколько строк.

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

Пример:

function arithmetic(a, b) {
  return `Это таблица с арифметическими операторами:
${a} + ${b} = ${a + b}
${a} - ${b} = ${a - b}
${a} * ${b} = ${a * b}
${a} / ${b} = ${a / b}`;
}

Не используйте продолжения строк

Не используйте продолжения строк (то есть завершение строки внутри строкового литерала обратным слешем) ни в обычных, ни в шаблонных строковых литералах. Хотя ES5 позволяет это, это может привести к неожиданным ошибкам, если любой пробельный символ стоит после косой черты — к тому же, он является менее очевидным для читателей.

Запрещено:

const longString = 'Это очень длинная строка, которая превышает лимит в \
    80 символов. К сожалению, она содержит длинные отрезки пустого пространства, так \
    как имеются отступы для поддержания форматирования.';

Вместо этого напишите

const longString = 'Это очень длинная строка, которая превышает лимит в ' +
    '80 символов. К сожалению, она содержит длинные отрезки пустого пространства, так ' +
    'как имеются отступы для поддержания форматирования.';

Числовые литералы

Числа могут быть указаны в десятичной, шестнадцатеричной, восьмеричной или двоичной форме. Используйте точные префиксы 0x0o и 0b со строчными буквами для шестнадцатеричных, восьмеричных и двоичных форм соответственно. Никогда не включайте ведущий ноль, если за ним не следуют xo или b.

Функции

Стрелочные функции

Стрелочные функции обеспечивают лаконичный синтаксис функций и упрощают поиск this для вложенных функций. Стрелочные функции более предпочтительны, чем ключевое слово function, особенно для вложенных функций.

Предпочтительнее использование стрелочных функций, чем других подходов для привязки this — такими как f.bind(this)goog.bind(f, this) и const self = this. Стрелочные функции особенно полезны для вызова callback'ов, так как они позволяют явно указывать, какие параметры должны передаваться обратному вызову, в то время как привязка (binding) будет слепо передавать все параметры.

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

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

В правой части от стрелки находится тело функции. По умолчанию тело - это блок инструкций (ноль или более инструкций, окруженных фигурными скобками). Тело также может быть неявно возвращенным выражением, если: или логика программы требует возврата значения, или оператор void предшествует вызову одной функции или метода (использование void обеспечивает возврат undefined, предотвращает утечку значений и сообщает о намерении). Форма выражения предпочтительнее, если она улучшает читабельность (например, для коротких или простых выражений).

Примеры:

/**
 * Стрелочная функция может быть задокументирована как обычная функция.
 * @param {number} numParam Число, которое нужно добавить
 * @param {string} strParam Еще одно число, которое нужно добавить
 * @return {number} Сумма двух параметров.
 */
const moduleLocalFunc = (numParam, strParam) => numParam + Number(strParam);

// Использует синтаксис выражения с `void`, потому что программная логика
// не требует возвращения значения.
getValue((result) => void alert(`Получено ${result}`));

class CallbackExample {
  constructor() {
    /** @private {number} */
    this.cachedValue_ = 0;

    // Для однострочных callback'ов, вы можете использовать определение типов для параметров.
    // Используется блок инстукций, потому что значение выражения не должно ничего
    // возвращать, и выражение не является единственным вызовом функции.
    getNullableValue((/** ?number */ result) => {
      this.cachedValue_ = result == null ? 0 : result;
    });
  }
}

Запрещено:

/**
 * Функция без параметров и без возвращаемого значения.
 * Такое использование тела выражения недопустимо, поскольку логика программы
 * не требует возвращаемого значение, и нам не хватает оператора `void`.
 */
const moduleLocalFunc = () => anotherFunction();

Вложенные функции и замыкания

Функции могут содержать вложенные определения функций. Если имеет смысл дать функции имя, функция должна быть присвоена локальной const

Генераторы

Генераторы позволяют использовать ряд полезных абстракций и могут быть использованы по мере необходимости.

При определении функций генератора допишите * к ключевому слову function, если оно присутствует, и отделите его пробелом от имени функции. При использовании делегированных yield допишите * к ключевому слову yield.

Пример:

/** @return {!Iterator<number>} */
function* gen1() {
  yield 42;
}

/** @return {!Iterator<number>} */
const gen2 = function*() {
  yield* gen1();
}

class SomeClass {
  /** @return {!Iterator<number>} */
  * gen() {
    yield 42;
  }
}

Параметры по умолчанию

Допускается использование опциональных параметров с помощью оператора равенства в списке параметров. Опциональные параметры должны включать пробелы с обеих сторон оператора равенства, именоваться точно так же, как и требуемые параметры, идти после требуемых параметров и не использовать инициализаторы, которые создают заметные побочные эффекты. Все опциональные параметры для функций должны иметь значения по умолчанию, даже если это значение undefined. В отличие от обычных функций, абстрактный и интерфейсный методы должны опускать значения параметров по умолчанию.

Пример:

/**
 * @param {string} required Этот параметр необходим всегда
 * @param {string=} optional Этот параметр может быть опущен
 * @param {!Node=} node Другой опциональный параметр
 */
function maybeDoSomething(required, optional = '', node = undefined) {}

/** @interface */
class MyInterface {
  /**
   * Интерфейсные и абстрактные методы должны опускать значения параметров по умолчанию.
   * @param {string=} optional
   */
  someMethod(optional) {}
}

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

Подсказка: Хотя в качестве инициализаторов могут использоваться произвольные выражения, включая вызовы функций, их следует делать как можно более простыми. Избегайте инициализаторов, показывающих общее изменяемое состояние, так как это может легко привести к непреднамеренной связи между вызовами функций

Остальные параметры (Rest parameters)

Используйте параметр rest вместо доступа к arguments.  Параметр rest должен быть последним в списке. Между ... и именем параметра нет пробела. Никогда не называйте локальную переменную или параметр arguments, он вносит путаницу из-за встроенного arguments.

Пример:

/**
 * @param {!Array<string>} array Это обычный параметр.
 * @param {...number} numbers Все оставшиеся параметры являются числами.
 */
function variadic(array, ...numbers) {}

Оператор расширения (Spread operator)

Вызовы функций могут использовать оператор Spread (...). Предпочитайте оператор Spread вместоFunction.prototype.apply при распаковке массива или итерабельного метода в несколько параметров функции с вариативным количеством параметров. Пробел после ... отсутствует.

Пример:

function myFunction(...elements) {}
myFunction(...array, ...iterable, ...generator());

Литералы массива

Используйте закрывающие запятые

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

Пример:

const values = [
  'first value',
  'second value',
];

Не используйте вариационный конструктор Array

Конструктор подвержен ошибкам при добавлении или удалении аргументов. Вместо этого используйте литерал.

Запрещено:

const a1 = new Array(x1, x2, x3);
const a2 = new Array(x1, x2);
const a3 = new Array(x1);
const a4 = new Array();

Это работает, как и ожидалось, за исключением третьего случая: если x1 целое число, то a3 — массив размером x1, где все элементы неопределены. Если x1 — любое другое число, то будет брошено исключение, а если это что-то другое, то это будет одноэлементный массив.

Вместо этого используйте

const a1 = [x1, x2, x3];
const a2 = [x1, x2];
const a3 = [x1];
const a4 = [];

Явное определение массива заданной длины с помощью new Array(length) разрешено, когда это необходимо.

Нецифровые свойства

Не определяйте и не используйте нецифровые свойства вместе с массивом (кроме length). Вместо этого используйте Map (или Object).

Деструктуризация

Для выполнения деструктуризации (например, при распаковке нескольких значений из одного массива или iterable), можно использовать литералы массива слева от определения . Заключительный rest элемент может быть включен в конец (без пробела между ... и именем переменной). Значения должны быть опущены, если они не используются.

const [a, b, c, ...rest] = generateResults();
let [, b,, d] = someArray;

Деструктуризация также может быть использована для параметров функции (обратите внимание, что имя параметра требуется, но игнорируется). Всегда указывайте [] в качестве значения по умолчанию, если параметр деструктурированного массива является необязательным, а также указывайте значения по умолчанию с левой стороны:

/** @param {!Array<number>=} param1 */
function optionalDestructuring([a = 4, b = 2] = []) { … };

Запрещено:

function badDestructuring([a, b] = [4, 2]) { … };

Совет: Для распаковки\упаковки нескольких значений в параметры функции или return, предпочитайте, когда это возможно, деструктуризацию объекта, а не массива, так как это позволяет именовать отдельные элементы и указывать для каждого из них свой тип.

Оператор расширения (Spread operator)

Литералы массива могут включать оператор Spread (...) для извлечения элементов из одного или нескольких iterables. Оператор Spread следует использовать вместо более неудобных конструкций с Array.prototype. Пробел после ... отсутствует.

Пример:

[...foo]   // предпочтительнее, чем Array.prototype.slice.call(foo)
[...foo, ...bar]   // предпочтительнее, чем foo.concat(bar)

Литерал объекта

Использование закрывающих запятых

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

Не используйте конструктор Object

Хотя Object не имеет тех же проблем, что и Array, для согласованности он все равно запрещен. Вместо этого используйте ({} или {a: 0, b: 1, c: 2}).

Не смешивайте ключи с кавычками и без

Объектные литералы могут представлять собой либо structs (с ключами и/или символами без кавычек), либо dicts (с и/или вычисляемыми ключами в кавычках). Не смешивайте эти типы ключей в одном объектном литерале.

Запрещено:

{
  width: 42, // ключ без кавычек в стиле struct
  'maxWidth': 43, // ключ с кавычками в стиле dict
}

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

Запрещено:

const o = {width: 42};
if (o.hasOwnProperty('maxWidth')) {
  ...
}

Лучше всего это реализовать как:

const o = {width: 42};
if (o.maxWidth != null) {
  ...
}

Вычисляемые имена свойств

Вычисляемые имена свойств (например, {['key' + foo()]: 42}) разрешены, заключаются в кавычки и считаются ключами в dict-стиле, (т.е. не должны смешиваться с ключами в кавычках), если только вычисленное свойство не является символом (например, [Symbol.iterator]). Значения перечислений также могут быть использованы для вычисляемых ключей, но не должны смешиваться с незнаковыми ключами в одном и том же литерале.

Сокращенное объявление метода

Методы могут быть определены в объектных литералах с помощью сокращенного варианта ({method() {… }}) вместо двоеточия, сразу за которым следует function или литерал стрелочной функции.

Пример:

return {
  stuff: 'candy',
  method() {
    return this.stuff;  // Вернет 'candy'
  },
};

Обратите внимание, что this в сокращенном объявлении метода или функции ссылается на сам литерал объекта, в то время как this в функции со стрелочном объявлением ссылается на область видимости, выходящую за рамки литерала объекта.

Пример:

class {
  getObjectLiteral() {
    this.stuff = 'fruit';
    return {
      stuff: 'candy',
      method: () => this.stuff,  // Вернет 'fruit'
    };
  }
}

Сокращенные свойства

В литералах объекта допустимы сокращенные свойства.

Пример:

const foo = 1;
const bar = 2;
const obj = {
  foo,
  bar,
  method() { return this.foo + this.bar; },
};
assertEquals(3, obj.method());

Деструктуризация

Деструктуризация объектов может быть использована слева от определения для распаковки нескольких значений из одного объекта.

Деструктурируемые объекты также могут использоваться в качестве параметров функции, но должны быть максимально простыми: может быть только один уровень свойств без кавычек. Более глубокие уровни вложенности и вычисляемые свойства не могут использоваться при деструктуризации параметров. Указывайте значения по умолчанию в левой части деструктурируемого параметра ({str = 'some default'} = {}, а не {str} = {str: some default'}), и если параметр сам по себе необязателен, то по умолчанию он должен иметь значение {}. JSDoc'y деструктурируемого параметра может быть присвоено любое имя (имя не используется, но требуется компилятору).

Пример:

/**
 * @param {string} ordinary
 * @param {{num: (number|undefined), str: (string|undefined)}=} param1
 *     num: Количество раз, сколько выполнить действие
 *     str: Описание того, что делать
 */
function destructured(ordinary, {num, str = 'some default'} = {})

Запрещено:

/** @param {{x: {num: (number|undefined), str: (string|undefined)}}} param1 */
function nestedTooDeeply({x: {num, str}}) {};
/** @param {{num: (number|undefined), str: (string|undefined)}=} param1 */
function nonShorthandProperty({num: a, str: b} = {}) {};
/** @param {{a: number, b: number}} param1 */
function computedKey({a, b, [a + b]: c}) {};
/** @param {{a: number, b: string}=} param1 */
function nontrivialDefault({a, b} = {a: 2, b: 4}) {};

Управляющие циклы

Для циклов

В ES6 язык теперь имеет три различных типа циклов for. Все они могут использоваться, хотя for-of цикл должен быть в приоритете, когда это возможно.

for-in циклы можно использовать только для объектов в dict стиле (см. ??), и их не следует использовать для итераций по массиву. Object.prototype.hasOwnProperty следует использовать в for-in циклах для исключения нежелательных свойств прототипа. Предпочтите for-of и Object.keys, чем for-in, когда это возможно.

Исключения

Исключения являются важной частью языка и должны использоваться всякий раз, когда возникают исключительные случаи. Всегда бросайте Error или подклассы Error: никогда не выбрасывайте строковые литералы или другие объекты. Всегда используйте new при построении Error.

Эта обработка распространяется на значения Promise rejection() , поскольку Promise.rejection(obj) эквивалентно throw obj; в асинхронных функциях.

Пользовательские исключения обеспечивают отличный способ передачи дополнительной информации об ошибках из функций. Они должны быть определены и использованы везде, где родной тип Error недостаточен.

Предпочтение отдается выбрасыванию исключений, а не специальным подходам к обработке ошибок (таким как передача ссылки на тип контейнера ошибки или возврат объекта со свойством ошибки).

Пустые catch блоки

Очень редко корректно ничего не делать в ответ на пойманное исключение. Когда действительно уместно не предпринимать никаких действий в блоке catch, причина, по которой это оправдано, объясняется в комментариях.

try {
  return handleNumericResponse(response);
} catch (ok) {
  // это не число; это нормально — продолжаем работу
}
return handleTextResponse(response);

Запрещено:

  try {
    shouldFail();
    fail('ожидается ошибка');
  } catch (expected) {
  }

Классы

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

Конструкторы являются необязательными. Конструкторы подклассов должны вызывать super() перед установкой любых полей или иным обращением к this. Интерфейсы должны объявлять неметодные свойства в конструкторе.

Не взаимодействуйте напрямую с prototype

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

Getters и Setters

Не используйте JavaScript getter и setter свойства. Они потенциально неочевидны и трудны в понимании, а также имеют ограниченную поддержку в компиляторе. Вместо этого предоставьте обычные методы.

Переопределение toString

Метод toString может быть переопределен, но должен всегда правильно выполняться и никогда не иметь видимых побочных эффектов.

Совет: Остерегайтесь, в частности, вызова других методов из toString, так как иногда это может привести к бесконечным циклам.

Запрещенные возможности

with

Не используйте ключевое слово with. Это сделает ваш код более трудным для понимания. К тому же он запрещен в строгом режиме со времен ES5.

Динамическая оценка кода

Не используйте eval для конструктора Function(...string) (за исключением загрузчиков кода). Эти возможности потенциально опасны и просто не работают в среде CSP (политики защиты контента).

Автоматическая установка точки с запятой

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

Нестандартные функции

Не используйте нестандартные функции. Сюда относятся старые функции, которые были удалены (например, WeakMap.clear), новые функции, которые еще не стандартизованы (например, текущий рабочий проект TC39, предложения на любом этапе, или предлагаемые, но не исчерпывающие веб-стандарты), или проприетарные функции, которые реализованы только в некоторых браузерах. Используйте только те функции, которые определены в текущих стандартах ECMA-262 или WHATWG. (Обратите внимание, что проекты, настаивающие против определенных API, такие как расширения Chrome или Node.js, очевидно, могут использовать эти API). Нестандартный языковые расширения (например, предоставляемые некоторыми внешними транспайлерами — source-to-source компиляторами) запрещены.

Объекты-обёртки для примитивных типов

Никогда не используйте new на обертках примитивных объектов (BooleanNumberStringSymbol), а также не включайте их в аннотации к типам.

Запрещено:

const /** Boolean */ x = new Boolean(false);
if (x) alert(typeof x);  // отобразить 'object'... — какого типа?

Обертки могут вызываться как функции для приведения типов (что предпочтительнее, чем использование + или конкатенация пустой строки) или создания символов.

Пример:

const /** boolean */ x = Boolean(0);
if (!x) alert(typeof x);  // отобразить 'boolean', как ожидается

Модификация встроенных объектов

Никогда не модифицируйте встроенные типы, добавляя методы в свои конструкторы или прототипы. Избегайте зависимости от библиотек, которые это делают. Обратите внимание, что библиотека исполнения JSCompiler'a по возможности предоставляет полифиллеры (преобразуют новый код в поддерживаемый старыми браузерами), соответствующие стандартам; больше ничто не должно модифицировать встроенные объекты.

Не добавляйте символы в глобальный объект, если только это не является абсолютно необходимым (например, требуется сторонним API).

Опускание (), когда вызываете конструктор

Никогда не вызывайте конструктор с оператором new без использования скобок ().

Запрещено:

new Foo;

Используйте вместо этого:

new Foo();

Опущенные скобки могут привести к едва уловимым ошибкам. Эти две строки не эквивалентны:

new Foo().Bar();
new Foo.Bar();

Стайлгайд по JS CSS Html c редакцией школы METHED -> Notion

JS CodeStyle from Google c редакцией школы METHED -> Notion

openImgPic