Как правильно пишется javascript

Время на прочтение
8 мин

Количество просмотров 254K

Недавно мы провели опрос, посвящённый целесообразности перевода этого руководства по JavaScript. Как оказалось, около 90% проголосовавших отнеслись к данной идее положительно. Поэтому сегодня публикуем первую часть перевода.

Это руководство, по замыслу автора, рассчитано на тех, кто уже немного знаком JavaScript и хочет привести свои знания в порядок а также узнать о языке что-то новое. Мы решили немного расширить аудиторию этого материала, включить в неё тех, кто совершенно ничего не знает о JS, и начать его с написания нескольких вариантов «Hello, world!».

→ Часть 1: первая программа, особенности языка, стандарты
→ Часть 2: стиль кода и структура программ
→ Часть 3: переменные, типы данных, выражения, объекты
→ Часть 4: функции
→ Часть 5: массивы и циклы
→ Часть 6: исключения, точка с запятой, шаблонные литералы
→ Часть 7: строгий режим, ключевое слово this, события, модули, математические вычисления
→ Часть 8: обзор возможностей стандарта ES6
→ Часть 9: обзор возможностей стандартов ES7, ES8 и ES9

Hello, world!

Программа, которую по традиции называют «Hello, world!», очень проста. Она выводит куда-либо фразу «Hello, world!», или другую подобную, средствами некоего языка.

JavaScript — это язык, программы на котором можно выполнять в разных средах. В нашем случае речь идёт о браузерах и о серверной платформе Node.js. Если до сих пор вы не написали ни строчки кода на JS и читаете этот текст в браузере, на настольном компьютере, это значит, что вы буквально в считанных секундах от своей первой JavaScript-программы.

Для того чтобы её написать, если вы пользуетесь Google Chrome, откройте меню браузера и выберите в нём команду Дополнительные инструменты > Инструменты разработчика. Окно браузера окажется разделённым на две части. В одной из них будет видна страница, в другой откроется панель с инструментами разработчика, содержащая несколько закладок. Нас интересует закладка Console (Консоль). Щёлкните по ней. Не обращайте внимания на то, что уже может в консоли присутствовать (для её очистки можете воспользоваться комбинацией клавиш Ctrl + L). Нас сейчас интересует приглашение консоли. Именно сюда можно вводить JavaScript-код, который выполняется по нажатию клавиши Enter. Введём в консоль следующее:

console.log("Hello, world!")

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

После того, как текст программы оказался в консоли, нажмём клавишу Enter.

Если всё сделано правильно — под этой строчкой появится текст Hello, world!. На всё остальное пока не обращайте внимания.

Первая программа в консоли браузера — вывод сообщения в консоль

Ещё один вариант браузерного «Hello, world!» заключается в выводе окна с сообщением. Делается это так:

alert("Hello, world!")

Вот результат выполнения этой программы.

Вывод сообщения в окне

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

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

Существуют и другие способы запуска JS-кода в браузере. Так, если говорить об обычном использовании программ на JavaScript, они загружаются в браузер для обеспечения работы веб-страниц. Как правило, код оформляют в виде отдельных файлов с расширением .js, которые подключают к веб-страницам, но программный код можно включать и непосредственно в код страницы. Всё это делается с помощью тега <script>. Когда браузер обнаруживает такой код, он выполняет его. Подробности о теге script можно посмотреть на сайте w3school.com. В частности, рассмотрим пример, демонстрирующий работу с веб-страницей средствами JavaScript, приведённый на этом ресурсе. Этот пример можно запустить и средствами данного ресурса (ищите кнопку Try it Yourself), но мы поступим немного иначе. А именно, создадим в каком-нибудь текстовом редакторе (например — в VS Code или в Notepad++) новый файл, который назовём hello.html, и добавим в него следующий код:

<!DOCTYPE html>
<html>
  <body>
    <p id="hello"></p>

    <script>
      document.getElementById("hello").innerHTML = "Hello, world!";
    </script>
  </body>
</html>

Здесь нас больше всего интересует строчка document.getElementById("hello").innerHTML = "Hello, world!";, представляющая собой JS-код. Этот код заключён в открывающий и закрывающий теги <script>. Он находит в документе HTML-элемент с идентификатором hello и меняет его свойство innerHTML (то есть — тот HTML код, который содержится внутри этого элемента) на Hello, world!. Если открыть файл hello.html в браузере, на ней будет выведен заданный текст.

Сообщение, выведенное средствами JavaScript в HTML-элемент

Как уже говорилось, примеры, приводимые на сайте w3school.com, можно тут же и попробовать. Существуют и специализированные онлайн-среды для веб-разработки, и, в частности, для выполнения JS-кода. Среди них, например codepen.io, jsfiddle.net, jsbin.com.

Вот, например, как выглядит наш пример, воссозданный средствами CodePen.

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

Выше мы говорили о том, что JavaScript-программы можно выполнять на различных платформах, одной из которых является серверная среда Node.js. Если вы собираетесь изучать JavaScript, ориентируясь именно на серверную разработку, вероятно, вам стоит запускать примеры именно средствами Node.js. Учтите, что, говоря упрощённо, и не учитывая особенности поддержки конкретных возможностей языка используемыми версиями Node.js и браузера, в Node.js и в браузере будет работать один и тот же код, в котором используются базовые механизмы языка. То есть, например, команда console.log("Hello, world!") будет работать и там и там. Программы, использующие механизмы, специфичные для браузеров, в Node.js работать не будут (то же самое касается и попыток запуска программ, рассчитанных на Node.js, в браузере).

Для того чтобы запустить наш «Hello, world!» в среде Node.js, установим Node.js, скачав отсюда подходящий дистрибутив. Теперь создадим файл hello.js и поместим в него следующий код:

console.log('Hello, World!');

Средствами командной строки перейдём в папку, в которой хранится этот файл, и выполним такую команду:

node hello.js

Вот каким будет результат её выполнения:

Сообщение, выведенное средствами Node.js

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

Теперь, после того, как состоялось ваше первое знакомство с JavaScript, предлагаем подробнее поговорить об этом языке.

Общие сведения о JavaScript

JavaScript — это один из самых популярных языков программирования в мире. Он, созданный более 20 лет назад, прошёл в своём развитии огромный путь. JavaScript задумывался как скриптовый язык для браузеров. В самом начале он обладал куда более скромными возможностями, чем сейчас. Его, в основном, использовали для создания несложных анимаций, вроде выпадающих меню, о нём знали как о части технологии Dynamic HTML (DHTML, динамический HTML).

Со временем потребности веб-среды росли, в частности, появлялись новые API, и JavaScript, для поддержки веб-разработки, нужно было не отставать от других технологий.

В наши дни JS используется не только в традиционных браузерах, но и за их пределами. В частности, речь идёт о серверной платформе Node.js, о возможностях по использованию JavaScript в разработке встраиваемых и мобильных приложений, о решении широкого спектра задач, для решения которых раньше JavaScript не использовался.

Основные характеристики JavaScript

JavaScript — это язык, который отличается следующими особенностями:

  • Высокоуровневый. Он даёт программисту абстракции, которые позволяют не обращать внимание на особенности аппаратного обеспечения, на котором выполняются JavaScript-программы. Язык автоматически управляет памятью, используя сборщик мусора. Разработчик, в результате, может сосредоточиться на решении стоящих перед ним задач, не отвлекаясь на низкоуровневые механизмы (хотя, надо отметить, это не отменяет необходимости в рациональном использовании памяти). Язык предлагает мощные и удобные средства для работы с данными различных типов.
  • Динамический. В отличие от статических языков программирования, динамические языки способны, во время выполнения программы, выполнять действия, которые статические языки выполняют во время компиляции программ. У такого подхода есть свои плюсы и минусы, но он даёт в распоряжение разработчика такие мощные возможности, как динамическая типизация, позднее связывание, рефлексия, функциональное программирование, изменение объектов во время выполнения программы, замыкания и многое другое.
  • Динамически типизированный. Типы переменных при JS-разработке задавать необязательно. В одну и ту же переменную можно, например, сначала записать строку, а потом — целое число.
  • Слабо типизированный. В отличие от языков с сильной типизацией, языки со слабой типизацией не принуждают программиста, например, использовать в неких ситуациях объекты определённых типов, выполняя, при необходимости, неявные преобразования типов. Это даёт больше гибкости, но JS-программы не являются типобезопасными, из-за этого усложняются задачи проверки типов (на решение этой проблемы направлены TypeScript и Flow).
  • Интерпретируемый. Широко распространено мнение, в соответствии с которым JavaScript является интерпретируемым языком программирования, что означает, что программы, написанные на нём, не нуждаются в компиляции перед выполнением. JS в этом плане противопоставляют таким языкам, как C, Java, Go. На практике же браузеры, для повышения производительности программ, выполняют компиляцию JS-кода перед его выполнением. Этот шаг, однако, прозрачен для программиста, он не требует от него дополнительных усилий.
  • Мультипарадигменный. JavaScript не навязывает разработчику использование какой-то конкретной парадигмы программирования, в отличие, например, от Java (объектно-ориентированное программирование) или C (императивное программирование). Писать JS-программы можно, используя объектно-ориентированную парадигму, в частности — применяя прототипы и появившиеся в стандарте ES6 классы. Программы на JS можно писать и в функциональном стиле, благодаря тому, что функции здесь являются объектами первого класса. JavaScript допускает и работу в императивном стиле, используемом в C.

Да, кстати, надо отметить, что у JavaScript и Java нет ничего общего. Это — совершенно разные языки.

JavaScript и стандарты

ECMAScript, или ES, это название стандарта, которым руководствуются разработчики JavaScript-движков, то есть — тех сред, где выполняются JS-программы. Различные стандарты вводят в язык новые возможности, говоря о которых нередко упоминают наименование стандартов в сокращённой форме, например — ES6. ES6 — это то же самое, что и ES2015, только в первом случае число означает номер версии стандарта (6), а во втором — год принятия стандарта (2015).

Сложилось так, что в мире веб-программирования очень долго был актуален стандарт ES3, принятый в 1999 году. Четвёртой версии стандарта не существует (в неё попытались добавить слишком много новых возможностей и так и не приняли). В 2009 году был принят стандарт ES5, который представлял собой прямо-таки огромное обновление языка, первое за 10 лет. После него, в 2011 году, был принят стандарт ES5.1, в нём тоже было немало нового. Весьма значительным, в плане новшеств, стал и стандарт ES6, принятый в 2015 году. Начиная с 2015 года, новые версии стандарта принимают каждый год.

Самой свежей версией стандарта на момент публикации этого материала является ES9, принятая в июне 2018 года.

Итоги

Сегодня мы написали «Hello, World!» на JavaScript, рассмотрели основные особенности языка и поговорили о его стандартизации. В следующий раз поговорим о стиле JavaScript-текстов и о лексической структуре программ.

Уважаемые читатели! Если вы, до чтения этого материала, не были знакомы с JavaScript, просим рассказать о том, получилось ли у вас запустить «Hello, world!».

О переводе

Оригинальное руководство по стилю Вы можете найти по ссылке — Google JavaScript Style Guide.
Репозиторий с кодом данного перевода находится здесь — Репозиторий перевода JS Style Guide

Если Вы нашли несоответствие, ошибку или неточность в переводе — все читатели данного перевода будут очень благодарны Вам за pull request с исправлением :).

1 Введение

Этот документ содержит полное описание стандартов
Google для исходного кода на языке JavaScript. JavaScript файл считается
написанным в стиле Google, если и только если он придерживается
правил, описанных в этом документе.

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

1.1 Терминологические примечания

В данном документе, если не указано другое:

  1. Термин комментарий всегда относится к комментарию
    реализации. Мы не используем фразу
    документирующий комментарий. Для этого мы используем общий
    термин “JSDoc”, включая текст, читаемый человеком, и
    аннотации, обрабатывающиеся компьютером при помощи
    /** … */.

  2. Данное руководство по стилю ссылается на терминологию
    RFC 2119, когда
    использует фразы «нужно», «не нужно», «не стоит» и «вероятно».
    Термины «отдавайте предпочтение» и «избегайте» используются в смысле
    «стоит» и «не стоит» делать соответственно, но не являются
    обязательными правилами.

Другие терминологические уточнения будут иногда появляться по
ходу документа.

1.2 Примечания по руководству

Примеры кода в этом документе не являются
нормативными (т.е. обязательными). Это значит, что
примеры в стиле Google могут и не иллюстрировать единственно правильный
вариант стилизации кода. Форматирование кода в данном файле не должно
приниматься в качестве правила.

2 Основы исходных файлов

2.1 Имя файла

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

2.2 Кодировка файлов UTF-8

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

2.3 Специальные символы

2.3.1 Пробельные символы

Помимо последовательности символов перевода строки, символ
горизонтального пробела (0х20) из таблицы символов ASCII — единственный
допустимый символ пробела, который может появляться где-либо в исходном
коде. Это подразумевает, что:

  1. Все другие пробельные символы экранируются.

  2. Отступ с помощью Tab не используется для отступа.

2.3.2 Специальные экранирующие последовательности

Любые символы, которые требуется экранировать специальными символьными
последовательностями (', ", \,
b, f, n, r,
t, v) — используют данные последовательности
вместо числового экранирования (например, x0a,
u000a или u{a}). Устаревшие восьмеричные
символы экранирования не используются.

2.3.3 Не-ASCII символы

Для оставшихся не-ASCII символов используется или подходящий символ
Unicode (например, ), или эквивалентная hex или
Unicode последовательность символов (например, u221e) в
зависимости от того, что делает код более
легко читаемым и более понятным.

Подсказка: при использовании Unicode-символов (или их
последовательностей), лучше использовать пояснительный комментарий.

        /* В идеале: код хорошо понятен даже без комментария. */
const units = 'μs';

/* Разрешено: но необязательно, так как μ — это печатаемый символ. */
const units = 'u03bcs'; // 'μs'

/* Хорошо: используется экранирование для непечатаемых символов с комментарием для большей ясности. */
return 'ufeff' + content;  // Добавляется знак "порядка байтов".
/* Плохо: читатель не понимает, что это за символ. */
const units = 'u03bcs';
      

Подсказка: никогда не делайте Ваш код менее читабельным просто из-за
страха, что некоторые программы могут некорректно обрабатывать не-ASCII
символы. В случае, если это произойдет — программа будет «сломана» и это
потребуется исправить (т.е. вы не упустите данный момент).

3 Структура исходных файлов

Все новые файлы должны быть или файлом goog.module (файл
содержащий вызов goog.module), или модулем ECMAScript
(использует операторы import и export). Файлы
состоят из следующего (по порядку):

  1. Информация о лицензии или авторских правах, если такие имеются
  2. @fileoverview JSDoc, если имеется
  3. Оператор goog.module, если это
    goog.module файл
  4. ES оператор import, если это ES модуль
  5. Операторы goog.require и goog.requireType
  6. Реализация файла

Только одна линия отделяет каждую секцию кода, за
исключением отделения реализации файла, которая должна отделяться 1 или
2 пустыми линиями.

3.1 Информация о лицензии или авторских правах, если имеется.

Если информация о лицензии или авторском праве находится в файле, она
находится здесь.

3.2 @fileoverview JSDoc, если имеется

Смотрите ?? о правилах
форматирования.

3.3 Оператор goog.module

Все goog.module файлы должны объявлять только одно имя для
goog.module в одном файле: линии, содержащие объявление
goog.module не должны разрываться и, следовательно, они
являются исключением из правила про лимит в 80 символов.

Аргумент goog.module определяет пространство имен. Это имя пакета
(идентификатор, который отражает фрагмент структуры каталогов, где
находится исходный код) и, опционально, главный
классперечислениеинтерфейс, который определяет связь со всем модулем.

Пример:

        goog.module('search.urlHistory.UrlHistoryService');
      

3.3.1 Иерархия

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

Не разрешается:

        goog.module('foo.bar');   // 'foo.bar.qux' было бы неплохо
goog.module('foo.bar.baz');
      

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

3.3.2 goog.module.declareLegacyNamespace

Оператор goog.module может опционально следовать до вызова
goog.module.declareLegacyNamespace();. Не используйте
goog.module.declareLegacyNamespace(), когда это возможно.

Пример:

        goog.module('my.test.helpers');
goog.module.declareLegacyNamespace();
goog.setTestOnly();
      

goog.module.declareLegacyNamespace существует для
облегчения перехода от традиционных пространств имен на основе иерархии
объектов, но имеет некоторые ограничения в плане именования. Поскольку
имя дочернего модуля должно быть создано после родительского
пространства имен, это имя не должно быть дочерним или
родительским по отношению к goog.module (например,
goog.module('parent'); и
goog.module('parent.child'); не могут существовать
одновременно, так же как и не может goog.module('parent'); и
goog.module('parent.child.grandchild');).

3.3.3 goog.module Экспорт

Классы, перечисления, функции, константы и другие символы
экспортируются, используя объект exports. Экспортируемые
символы могут быть определены прямо в объекте exports или
определяться локально, а потом экспортироваться раздельно. Символы
экспортируются только тогда, когда они должны использоваться за
пределами модуля. Не экспортируемые модульно-локальные символы не
декларируются как @private и не заканчиваются нижней
чертой. Определенного порядка для экспортируемых модульно-локальных
символов — нет.

Пример:

        const /** !Array<number> */ exportedArray = [1, 2, 3];

const /** !Array<number> */ moduleLocalArray = [4, 5, 6];

/** @return {number} */
function moduleLocalFunction() {
  return moduleLocalArray.length;
}

/** @return {number} */
function exportedFunction() {  
  return moduleLocalFunction() * 2;
}

exports = {exportedArray, exportedFunction};
/** @const {number} */
exports.CONSTANT_ONE = 1;

/** @const {string} */
exports.CONSTANT_TWO = 'Другая константа';
      

Не аннотируйте exports с помощью @const, так
как они уже рассматриваются компилятором в качестве констант.

        /** @const */
exports = {exportedFunction};
      

3.4 ES модули

3.4.1 Импорт

Операторы импорта не должны разделяться переносом строки и,
следовательно, являются исключением из ограничения в 80 символов.

3.4.1.1 Импорт путей

Файлы ES модуля должны использовать оператор import для
импорта файлов из ES модуля. Не используйте
goog.require для импорта другого ES модуля.

        import './sideeffects.js';
          
import * as goog from '../closure/goog/goog.js';
import * as parent from '../parent.js';

import {name} from './sibling.js';
      
3.4.1.1.1 Расширения файлов в для импортируемых путей

Расширение .js не является опциональным в импортируемых
путях и всегда должно присутствовать.

import '../directory/file';
import '../directory/file.js';
3.4.1.2 Импорт одного файла несколько раз

Не импортируйте один и тот же файл несколько раз. Это может затруднить
определение совокупности всех импортов в файле.

        // Импорт имеет один и тот же путь, но, поскольку он не выравнивается, может быть трудно его заметить.
import {short} from './long/path/to/a/file.js';
import {aLongNameThatBreaksAlignment} from './long/path/to/a/file.js';
      
3.4.1.3 Именование импортов
3.4.1.3.1 Именование модульных импортов

Имена импорта модуля (import * as name) — это имена в
нижнемВерблюжьемСтиле, которые получены из имени
импортируемого файла.

        import * as fileOne from '../file-one.js';
import * as fileTwo from '../file_two.js';
import * as fileThree from '../filethree.js';
      
        import * as libString from './lib/string.js';
import * as math from './math/math.js';
import * as vectorMath from './vector/math.js';
      
3.4.1.3.2 Именование импортов по умолчанию

Имена импорта по умолчанию получаются из имени импортируемого файла и
следуют правилам из ??.

        import MyClass from '../my-class.js';
import myFunction from '../my_function.js';
import SOME_CONSTANT from '../someconstant.js';
      

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

3.4.1.3.3 Именование именованного импорта

В целом, символы, импортируемые через именованный импорт (import {name}), должны иметь одинаковое имя. Избегайте импорта псевдонимов (import {SomeThing as SomeOtherThing}). Отдавайте предпочтение исправлению конфликтов имен при помощи импорта
модуля (import *) или переименовывая сами экспорты.

        import * as bigAnimals from './biganimals.js';
import * as domesticatedAnimals from './domesticatedanimals.js';

new bigAnimals.Cat();
new domesticatedAnimals.Cat();
      

Если необходимо переименовать именованный импорт, используйте компоненты
имени файла или путь в полученном псевдониме.

        import {Cat as BigCat} from './biganimals.js';
import {Cat as DomesticatedCat} from './domesticatedanimals.js';

new BigCat();
new DomesticatedCat();
      

3.4.2 Экспорт

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

3.4.2.1 Именованный экспорт vs экспорт по умолчанию

Используйте именованный экспорт во всем коде. Вы можете применить
ключевое слово export в объявлении или использовать
синтаксис export {name};.

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

        // Не используйте экспорт по умолчанию:
export default class Foo { ... } // Плохо!
      
        // Используйте именованный экспорт:
export class Foo { ... }
      
        // Альтернативный стиль с именем экспорта:
class Foo { ... }

export {Foo};
      
3.4.2.2 Экспорт статических контейнерных классов и объектов

Не экспортируйте классы контейнеров или объекты со статическими методами
или свойствами для пространства имен.

        // container.js
// Плохо: контейнер — это экспортируемый класс, который имеет только статические методы и поля.
export class Container {
  /** @return {number} */
  static bar() {
    return 1;
  }
}

/** @const {number} */
Container.FOO = 1;
      

Вместо этого экспортируйте отдельные константы и функции:

        /** @return {number} */
export function bar() {
  return 1;
}

export const /** number */ FOO = 1;
      
3.4.2.3 Мутабельность экспорта

Экспортируемые переменные не должны быть видоизменены за пределами
инициализации модуля.

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

// Плохо: и foо, и mutateFoo экспортируются и могут быть изменены.
export let /** number */ foo = 0;

/**
 * Изменяется foo. 
 */
 export function mutateFoo() {
  ++foo;
}

/**
 * @param {function(number): number} newMutateFoo 
 */
 export function setMutateFoo(newMutateFoo) {
  // Экспортируемые классы и функции могут быть видоизменены!
  mutateFoo = () => {
    foo = newMutateFoo(foo);  
  };
}
      
        // Хорошо: вместо того, чтобы экспортировать изменяемые переменные foo и mutateFoo напрямую,
// вместо этого сделайте их модульными и экспортируйте getter для foo и оболочку для
// mutateFooFunc.
let /** number */ foo = 0;
let /** function(number): number */ mutateFooFunc = foo => foo + 1;

/** @return {number} */
export function getFoo() {
  return foo;
}

export function mutateFoo() {
  foo = mutateFooFunc(foo);
}

/** @param {function(number): number} mutateFoo */
export function setMutateFoo(mutateFoo) {
  mutateFooFunc = mutateFoo;
}
      
3.4.2.4 export from

Операторы export from не должны быть перенесены на другую
строку и, следовательно, являются исключением из ограничения в 80
символом. Это относится ко всем export from операторам.

        export {specificName} from './other.js';
export * from './another.js';
      

3.4.3 Циклические зависимости в ES модулях

Не создавайте циклы между модулями ES, даже если спецификация ECMAScript
позволяет это. Обратите внимание, что можно создавать циклы как с
помощью операторов import, так и export.

        // a.js
import './b.js';
      
        // b.js
import './a.js';

// `export from` тоже может вызвать циклические зависимости!
export {x} from './c.js';
      
        // c.js
import './b.js';

export let x;
      

3.4.4 Взаимодействие с Closure

3.4.4.1 Ссылка на goog

Для того, чтобы сослаться на пространство имён Closure
goog, импортируйте goog.js из Closure.

        import * as goog from '../closure/goog/goog.js';

const name = goog.require('a.name');

export const CONSTANT = name.compute();
      

goog.js экспортирует только подмножество параметров из
глобального goog, которые могут использоваться в ES
модулях.

3.4.4.2 goog.require в ES модулях

goog.require в ES модулях работает так же, как и в
goog.module файлах. Вы можете потребовать (require) любой
символ пространства имён из Closure (т.е. символы, созданные с помощью
goog.provide или goog.module), и
goog.require вернёт значение.

        import * as goog from '../closure/goog/goog.js';
import * as anEsModule from './anEsModule.js';

const GoogPromise = goog.require('goog.Promise');
const myNamespace = goog.require('my.namespace');
      
3.4.4.3 Декларирование Closure ID модулей в ES модулях

goog.declareModuleId может использоваться в ES модулях,
чтобы декларировать ID модуля в стиле goog.module. Это
значит, что ID модуля может быть потребован с помощью
goog.require, goog.module.get,
goog.forwardDeclare и т.п., как будто это
goog.module, который не вызывает
goog.module.declareLegacyNamespace. Это не делает ID модуля
глобально доступным JavaScript символом.

goog.require (или goog.module.get) примененный
для ID модуля из goog.declareModuleId — будет всегда
возвращать объект модуля (как будто используется import *).
Как результат, аргумент для goog.declareModuleId должен
всегда заканчиваться именемНижнегоРегистра.

Обратите внимание: ошибка — вызывать
goog.module.declareLegacyNamespace в ES модуле, он может
быть вызван только из файлов goog.module. Не существует
прямых путей, чтобы ассоциировать устаревшее пространство имён с
ES модулем.

goog.declareModuleId должен использоваться только для того,
чтобы обновить Closure файлы в месте, где используется именованный
экспорт.

        import * as goog from '../closure/goog.js';
goog.declareModuleId('my.esm');
export class Class {};
      

3.5 goog.setTestOnly

В файле модуля goog.module оператор
goog.module может опционально сопровождаться вызовом
goog.setTestOnly().

В ES модуле оператор import может опционально
сопровождаться вызовом goog.setTestOnly().

3.6 Операторы goog.require и goog.requireType

Импорты, реализованные с помощью операторов goog.require и
goog.requireType. Имена, импортированные с помощью оператора
goog.require, могут быть использованы и в коде, и в
аннотациях, в то время как импортированные с помощью
goog.requireType могут быть использованы в аннотациях
типа.

Операторы goog.require и
goog.requireType формируют единый блок без пустых линий
внутри. Этот блок следует за декларацией goog.module,
разделённой
с помощью одной пустой линии.
Единственный аргумент для goog.require или
goog.requireType — это пространство имен, определенное с
помощью goog.module в отдельном файле. Операторы
goog.require и goog.requireType не должны
появляться в других местах данного файла.

Каждый goog.require или
goog.requireType определяется в виде отдельного
константного псевдонима или деструктурируется в несколько константных
псевдонимов. Эти псевдонимы — единственный допустимый способ сослаться на
зависимости в коде или аннотациях типа. Полные имена пространства имен
не должны использоваться нигде, кроме как в качестве аргумента для
goog.require или goog.requireType.

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

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

Исключение: В некоторых случаях, дополнительные
компоненты пространства имен могут использоваться для формирования более
длинных псевдонимов. Полученный псевдоним должен сохранять регистр
исходного идентификатора, чтобы он по-прежнему правильно идентифицировал
свой тип. Более длинные псевдонимы могут использоваться для устранения
неоднозначности идентичных псевдонимов, или если это значительно улучшает
читабельность. Кроме того, для предотвращения маскирования нативных
типов, таких как Element, Event,
Error, Map и Promise (см. более
полный список в
Стандартные встроенные объекты
и MDN
Web APIs). При переименовании деструктурированных псевдонимов пробел должен
следовать за двоеточием, как требуется в
??.

Файл не должен содержать оба оператора goog.require и
goog.requireType для одного и того же пространства имен.
Если импортированное имя используется как в коде, так и в аннотациях
типа, оно должно быть импортировано одним оператором
goog.require.

Если модуль импортируется только для его побочных эффектов, вызов должен
быть сделан при помощи goog.require (а не
goog.requireType), а назначение может быть опущено. Также
необходим комментарий, чтобы объяснить, почему это необходимо, и
подавить предупреждение компилятора.

Строки сортируются в соответствии со следующими правилами: Все
require с именем слева идут на первом месте,
отсортированными по алфавиту. Затем require с
деструктуризацией, снова отсортированные по названиям слева. Наконец,
любые вызовы require, которые являются автономными (обычно
модули, импортированные только для побочных эффектов).

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

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

Пример:

        // Стандартный стиль псевдонимов.
const MyClass = goog.require('some.package.MyClass');
const MyType = goog.requireType('some.package.MyType');
// Псевдоним на основе пространства имен, используемый для устранения неоднозначности.
const NsMyClass = goog.require('other.ns.MyClass');
// Псевдоним на основе пространства имен, используемый для предотвращения маскирования собственного типа.
const RendererElement = goog.require('web.renderer.Element');
// Непоследовательные псевдонимы на основе пространства имен используются для улучшения читабельности.
// Кроме того, строки, содержащие более 80 столбцов, не должны переноситься.
const SomeDataStructureModel = goog.requireType('identical.package.identifiers.models.SomeDataStructure');
const SomeDataStructureProto = goog.require('proto.identical.package.identifiers.SomeDataStructure');
// Стандартный стиль псевдонима.
const asserts = goog.require('goog.asserts');
// Псевдоним на основе пространства имен, используемый для устранения неоднозначности.
const testingAsserts = goog.require('goog.testing.asserts');
// Стандартная деструктуризация в псевдонимы.
const {clear, clone} = goog.require('goog.array');
const {Rgb} = goog.require('goog.color');
// Деструктуризация на основе пространства имен в псевдонимы для устранения неоднозначности.
const {SomeType: FooSomeType} = goog.requireType('foo.types');
const {clear: objectClear, clone: objectClone} = goog.require('goog.object');
// goog.require без псевдонима, чтобы вызвать побочные эффекты.
/** @suppress {extraRequire} Инициализируется MyFramework. */
goog.require('my.framework.initialization');
      

Неоднозначно:

// Если необходимо устранить неоднозначность, предпочтите PackageClass, а не SomeClass,
// так как он ближе к формату имени модуля.
const SomeClass = goog.require('some.package.Class');
      

Запрещено:

        // Дополнительные термины должны исходить из пространства имен.
const MyClassForBizzing = goog.require('some.package.MyClass');
// Псевдоним должен включать все конечные компоненты пространства имен.
const MyClass = goog.require('some.package.MyClassForBizzing');
// Псевдоним не должен маскировать собственный тип (здесь должно быть `const JspbMap`).
const Map = goog.require('jspb.Map');
// Не переносите goog.require строки более 80 символов.
const SomeDataStructure =
    goog.require('proto.identical.package.identifiers.SomeDataStructure');
// Псевдоним должен быть основан на пространстве имен.
const randomName = goog.require('something.else');
// Отсутствует пробел после двоеточия.
const {Foo:FooProto} = goog.require('some.package.proto.Foo');
// goog.requireType без псевдонима.
goog.requireType('some.package.with.a.Type');

/** 
 * @param {!some.unimported.Dependency} param Все внешние типы, использующиеся в JSDoc 
 *    аннотациях, должны быть потребованы с помощью goog.require, если не объявлено в externs. 
 */
 function someFunction(param) {
  // goog.require строки должны быть на верхнем уровне перед любым другим кодом.
  const alias = goog.require('my.long.name.alias');
  // ...
}
      

3.7 Реализация файла

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

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

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

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

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

4.1 Скобки

4.1.1 Скобки используются для всех управляющих структур

Скобки требуются для всех управляющих структур (например,
if, else, for, do,
while и т.п.), даже если тело содержит один оператор.
Первый оператор непустого блока должен начинаться с отдельной строки.

Не разрешено:

if (someVeryLongCondition()) doSomething();
for (let i = 0; i < foo.length; i++) bar(foo[i]);

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

if (shortCondition()) foo();

4.1.2 Непустые блоки: стиль K&R

Скобки следуют стилю Кернигана и Ричи (египетские скобки) для непустых блоков и блочных конструкций, если:

  • Никакого разрыва строки перед открывающей скобкой.
  • Разрыв строки после открывающей скобки.
  • Разрыв строки перед закрывающей скобкой.
  • Разрыв строки после закрывающей скобки if. Данная фигурная
    скобка завершает оператор или тело оператора функции или класса или
    метод класса. В частности, после фигурной скобки нет разрыва
    строки, если за ней следует else, catch,
    while или запятая, точка с запятой или правая скобка.

Пример:

class InnerClass {
  constructor() {}

  /** @param {number} foo */
  method(foo) {
    if (condition(foo)) {
      try {
        // Внимание: это может не сработать.
        something();
      } catch (err) {
        recover();
      }
    }
  }
}

4.1.3 Пустые блоки: могут быть краткими

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

Пример:

function doNothing() {}

Запрещено:

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

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

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

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

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

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

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

const b =
    [0, 1, 2];

const c = [0, 1, 2];

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

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

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

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

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);

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

Литералы класса (будь то объявления или выражения) имеют отступ в виде
блоков. Не добавляйте точку с запятой после методов или после
закрывающей скобки объявления класса (операторы, такие как
присваивания, которые содержат выражения класса, по-прежнему
заканчиваются точкой с запятой). Используйте ключевое слово
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) {}
}

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

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

Пример:

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();
      }
    });

4.2.5 Switch оператор

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

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

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

Пример:

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

  case Animal.JABBERWOCK:
    handleJabberwock();
    break;

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

4.3 Операторы

4.3.1 Один оператор на строку

После каждого оператора делается перевод строки.

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

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

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

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

Исключения:

  1. Операторы goog.module, goog.require и
    goog.requireType (см.
    ?? и
    ??).
  2. ES модули import и операторы
    export from (см. ?? и
    ??).
  3. Линии, в которых соблюдение предела столбцов невозможно или может
    препятствовать обнаружению. Примеры включают в себя:

    • Длинный URL, который должен быть кликабельным в источнике.
    • Команда консоли, предназначенная для копирования и вставки
    • Длинный строковый литерал, который может потребоваться полностью
      скопировать или найти (например, длинный путь к файлу).

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

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

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

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

Совет: извлечение метода или локальной переменной может решить проблему
без необходимости переноса строки.

4.5.1 Когда переносить

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

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

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

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

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

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

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

  1. Когда у оператора разрывается строка, разрыв следует за оператором.
    (Обратите внимание, что это не та же практика, что и в стиле Google
    для Java.)

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

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

4.5.2 Отступ строк с протяженностью не менее +4 пробелов

При переносе строк каждая строка после первой (каждая
строка продолжения ) имеет отступ не менее +4 от исходной
строки, если только она не подпадает под правила отступа блока.

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

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

4.6 Пробелы

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

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

  1. Между последовательными методами в литерале класса или объекта

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

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

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

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

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

  1. Отделение любого зарезервированного слова (например, if,
    for или 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)).

4.6.3 Горизонтальное выравнивание: не рекомендуется

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

Эта практика разрешена, но, как правило,
не рекомендуется в Google Style. Даже не требуется
поддерживать горизонтальное выравнивание в местах, где оно уже
использовалось.

Вот пример без выравнивания, за которым следует пример с выравниванием.
Оба разрешены, но последний не рекомендуется:

{
  tiny: 42, // хорошо 
  longer: 435, // тоже хорошо
};

{
  tiny:   42,  // разрешено, но требует будущего редактирования 
  longer: 435, // может остаться без выравнивания
};

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

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

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

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

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

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

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

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

Не используйте лишние скобки вокруг всего выражения после
delete, typeof, void,
return, throw, case,
in, of или yield.

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

В этом разделе рассматриваются комментарии реализации. JSDoc
рассматривается отдельно в ??.

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

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

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

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

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

Не используйте JSDoc (/** … */) для комментариев,
описывающих реализацию.

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

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

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

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

5 Языковые особенности

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

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

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

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

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

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

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

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

5.1.4 Объявлять типы по мере необходимости

Аннотации типа JSDoc могут быть добавлены либо в строке над объявлением,
либо в строке перед именем переменной, если нет других JSDoc.

Пример:

const /** !Array<number> */ data = [];

/**
 * Какое-то описание.
 * @type {!Array<number>}
 */
const data = [];

Смешивание строчных (inline) и JSDoc стилей не допускается: компилятор
будет обрабатывать только первые JSDoc комментарии, и строчные будут
потеряны.

/** Какое-то описание. */
const /** !Array<number> */ data = [];

Подсказка: Существует множество случаев, когда компилятор может сделать
вывод о шаблонизированном типе, но не о его параметрах. В частности, это
происходит, когда вызывающий литерал или конструктор не включает в себя
никаких значений с типом параметра (например, пустые массивы, объекты,
Map или Set), или если переменная изменяется в
рамках замыкания. Особенно полезны в этих случаях аннотации к типам
локальных переменных, так как в противном случае компилятор выдаст
параметр шаблона как неизвестный (unknown).

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

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

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

Пример:

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

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

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

Запрещено:

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) разрешено, когда это необходимо.

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

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

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

Для выполнения деструктуризации (например, при распаковке нескольких
значений из одного массива или 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, предпочитайте, когда это возможно,
деструктуризацию объекта, а не массива, так как это позволяет именовать
отдельные элементы и указывать для каждого из них свой тип.

5.2.5 Оператор расширения

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

Пример:

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

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

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

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

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

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

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

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

Запрещено:

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

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

Запрещено:

/** @type {{width: number, maxWidth: (number|undefined)}} */
const o = {width: 42};
if (o.hasOwnProperty('maxWidth')) {
  ...
}

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

/** @type {{width: number, maxWidth: (number|undefined)}} */
const o = {width: 42};
if (o.maxWidth != null) {
  ...
}

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

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

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

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

Пример:

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

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

Пример:

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

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

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

Пример:

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

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

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

Деструктурируемые объекты также могут использоваться в качестве
параметров функции, но должны быть максимально простыми: может быть
только один уровень свойств без кавычек. Более глубокие уровни
вложенности и вычисляемые свойства не могут использоваться при
деструктуризации параметров. Указывайте значения по умолчанию в левой
части деструктурируемого параметра ({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}) {};

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

5.3.8 Перечисления

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

/**
 * Поддерживаемые температурные шкалы.
 * @enum {string}
 */
const TemperatureScale = {
  CELSIUS: 'celsius',
  FAHRENHEIT: 'fahrenheit',
};

/**
 * Перечисление с двумя вариантами.
 * @enum {number}
 */
const Option = {
  /** Используемая опция должна быть первой. */
  FIRST_OPTION: 1,
  /** Второй среди двух вариантов. */
  SECOND_OPTION: 2,
};

5.4 Классы

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

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

5.4.2 Поля (fields)

Устанавливайте все поля объекта (т.е. все свойства, кроме методов) в
конструкторе. Аннотируйте поля, которые никогда не переназначаются, с
помощью @const. Аннотируйте непубличные поля подходящими
аннотациями видимости (@private, @protected,
@package) и заканчивайте все @private имена
полей подчеркиванием. Поля никогда не устанавливаются в конкретный
prototype класса.

Пример:

class Foo {
  constructor() {
    /** @private @const {!Bar} */
    this.bar_ = computeBar();

    /** @protected @const {!Baz} */
    this.baz = computeBaz();
  }
}

Подсказка: Свойства никогда не следует добавлять или удалять из
экземпляра класса после завершения работы конструктора, так как это
существенно затрудняет работу виртуальной машины (которая пытается
провести оптимизацию). При необходимости, поля, которые инициализируются
позже, должны быть явно установлены как
undefined в конструкторе для предотвращения последующих
изменений класса. Добавление @struct в объект будет
проверять, что необъявленные свойства не добавляются/запрашиваются.
Классы добавляют это по умолчанию.

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

Вычисляемые свойства могут использоваться в классах только в том случае,
если свойство является символом. Свойства в dict-стиле (то есть,
вычисляемые не символьные или заключенные в кавычки ключи, как определено
в
??), не допускаются. Метод
[Symbol.iterator] должен быть определен для любых классов,
логически итерабельных. Кроме того, Symbol следует
использовать экономно.

Совет: будьте осторожны с использованием любых других встроенных
символов (например, Symbol.isConcatSpreadable), так как они
не заменяются (полифилируются) компилятором и поэтому не будут работать
в старых браузерах.

5.4.4 Статические методы

Там, где это не мешает читабельности, предпочитайте модульно локальные
функции, а не приватные статические методы.

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

Запрещено:

class Base { /** @nocollapse */ static foo() {} }
class Sub extends Base {}
function callFoo(cls) { cls.foo(); }  // запрещено: не вызывайте статические методы динамически
Sub.foo();  // запрещено: не вызывайте статические методы в подклассах, которые сами их не определили

5.4.5 Объявление классов в старом стиле

Хотя классы ES6 являются предпочтительными, есть случаи, когда классы
ES6 не могут быть использованы. Например:

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

  2. Фреймворки, которым необходимо значение
    this перед вызовом конструктора суперкласса, поскольку
    конструкторы с ES6 суперклассами не имеют доступа к экземпляру
    this до тех пор, пока не завершится вызов
    super.

Во всех других случаях: let, const, параметры
по умолчанию, стрелочные функции и др. должны использоваться там, где
это возможно.

goog.defineClass допускает определение, похожее на
определение класса, аналогичное синтаксису класса ES6:

let C = goog.defineClass(S, {
  /**
   * @param {string} value
   */
  constructor(value) {
    S.call(this, 2);
    /** @const */
    this.prop = value;
  },

  /**
   * @param {string} param
   * @return {number}
   */
  method(param) {
    return 0;
  },
});

В качестве альтернативы, в то время как
goog.defineClass должен быть предпочтительным для всего
нового кода, более традиционный синтаксис также разрешен.

/**
  * @constructor @extends {S}
  * @param {string} value
  */
function C(value) {
  S.call(this, 2);
  /** @const */
  this.prop = value;
}
goog.inherits(C, S);

/**
 * @param {string} param
 * @return {number}
 */
C.prototype.method = function(param) {
  return 0;
};

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

Правильно определить иерархии конструкторов прототипа сложнее, чем
кажется на первый взгляд! По этой причине лучше всего использовать
goog.inherits из
Google Closure.

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

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

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

5.4.7 Getters и Setters

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

Исключения: бывают ситуации, когда определение геттера
или сеттера неизбежно (например, фреймворки с привязкой данных, такие
как Angular и Polymer, или для совместимости с внешними API, который
невозможно настроить). Только в этих случаях можно использовать геттеры
и сеттеры с осторожностью и при условии, что они определены с
помощью короткого вида записи метода get и
set или Object.defineProperties (не
Object.defineProperty, что мешает переименованию свойств).
Геттеры не должны изменять видимое состояние.

Запрещено:

class Foo {
  get next() { return this.nextId++; }
}

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

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

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

5.4.9 Интерфейсы

Интерфейсы могут быть объявлены с помощью @interface или
@record. Интерфейсы, объявленные с помощью
@record, могут быть явно (т.е. через
@implements) или неявно реализованы классом или литералом
объекта.

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

Пример:

/**
 * Что-то, с чем можно взаимодействовать
 * @record
 */
class Frobnicator {
  constructor() {
    /** @type {number} Количество попыток до завершения */
    this.attempts;
  }

  /**
   * Выполняет взаимодействие в соответствии с заданной стратегией
   * @param {!FrobnicationStrategy} strategy
   */
  frobnicate(strategy) {}
}

5.4.10 Абстрактные классы

При необходимости используйте абстрактные классы. Абстрактные классы и
методы должны быть аннотированы с помощью @abstract. Не
используйте goog.abstractMethod. См.
абстрактные классы и методы.

5.5 Функции

5.5.1 Функции верхнего уровня

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

Примеры:

/** @param {string} str */
exports.processString = (str) => {
  // Обработка строки.
};
/** @param {string} str */
const processString = (str) => {
  // Обработка строки
};

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

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

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

Стрелочные функции обеспечивают лаконичный синтаксис функций и упрощают
поиск 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();

5.5.4 Генераторы

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

При определении функций генератора допишите * к ключевому
слову 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;
  }
}

5.5.5 Параметры и возвращаемые типы

Параметры функции и возвращаемые типы обычно должны быть
задокументированы с помощью JSDoc аннотаций. Смотрите
?? для получения
дополнительной информации.

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

Допускается использование опциональных параметров с помощью оператора
равенства в списке параметров. Опциональные параметры должны включать
пробелы с обеих сторон оператора равенства, именоваться точно так же,
как и требуемые параметры (т.е. не префиксоваться с помощью
opt_), использовать суффикс = в их JSDoc-типе,
идти после требуемых параметров и не использовать инициализаторы,
которые создают заметные побочные эффекты. Все опциональные параметры
для функций должны иметь значения по умолчанию, даже если это значение
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, когда есть более чем один опциональный параметр, которые не имеют
естественного порядка.

Примечание: В отличие от параметров по умолчанию в Python, можно
использовать инициализаторы, которые возвращают новые мутируемые объекты
(такие как {} или []), потому что
инициализатор вычисляется каждый раз при использовании значения по
умолчанию, поэтому один объект не будет совместно использоваться в
нескольких вызовах.

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

5.5.5.2 Остальные параметры

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

Пример:

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

5.5.6 Обобщения

Объявите общие функции и методы, когда это необходимо, с помощью
@template TYPE в JSDoc над определением функции или метода.

5.5.7 Оператор расширения

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

Пример:

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

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

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

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

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

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

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

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

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

Пример:

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

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

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

Запрещено:

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

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

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

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

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

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

5.8.1 Для циклов

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

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

5.8.2 Исключения

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

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

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

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

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

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

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

Запрещено:

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

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

5.8.3 Switch оператор

Терминологическое примечание: Внутри фигурных скобок блока switch
находятся одна или несколько групп операторов. Каждая группа операторов
состоит из одной или нескольких блоков switch (либо
case FOO: или default:), за которыми следует
один или несколько операторов.

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

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

Пример:

switch (input) {
  case 1:
  case 2:
    prepareOneOrTwo();
    // fall through (продолжение)
  case 3:
    handleOneTwoOrThree();
    break;
  default:
    handleLargeNumber(input);
}
5.8.3.2 Блок default должен присутствовать

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

5.9 this

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

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

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

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

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

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

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

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

5.11.1 with

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

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

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

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

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

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

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

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

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

Запрещено:

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

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

Пример:

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

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

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

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

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

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

Запрещено:

new Foo;

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

new Foo();

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

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

6 Именование

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

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

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

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

Запрещено:

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

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

6.2.1 Именование пакетов

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

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

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

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

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

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

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

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

6.2.4 Имена перечислений

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

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

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

6.2.5.1 Определение “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');

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

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

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

Примеры:

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

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

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

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

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

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

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

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

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

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

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

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

6.2.10 Модульно-локальные имена

Имена в модулях, которые не экспортируются, являются неявно закрытыми.
Они не помечены @private и не заканчиваются подчеркиванием.
Это относится к классам, функциям, переменным, константам, перечислениям
и другим модульно-локальным идентификаторам.

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

Иногда существует более одного разумного способа преобразования
английской фразы в верблюжий стиль, например, когда присутствуют
аббревиатуры или необычные конструкции вроде 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. В конце, соедините все слова в один идентификатор.

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

Примеры:

Изначальная форма Корректно Некорректно
XML HTTP request XmlHttpRequest XMLHTTPRequest
new customer ID newCustomerId newCustomerID
inner stopwatch innerStopwatch innerStopWatch
supports IPv6 on iOS? supportsIpv6OnIos supportsIPv6OnIOS
YouTube importer YouTubeImporter YoutubeImporter*

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

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

7 JSDoc

JSDoc
используется для всех классов, полей и методов.

7.1 Общая форма

Основной вид форматирования блоков JSDoc показан в этом примере:

/**
 * Несколько линий текста JSDoc написаны здесь.
 * Строки переносятся, как обычно.
 * @param {number} arg Число, чтобы делать что-то.
 */
function doSomething(arg) { … }

или в этом однострочном примере:

/** @const @private {!Foo} Чуть меньше JSDoc. */
this.foo_ = foo;

Если однострочный комментарий переносится на несколько строк, он должен
использовать многострочный стиль с /** и */ в
своих строках.

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

7.2 Markdown

JSDoc написан на языке Markdown, хотя при необходимости может включать
HTML.

Обратите внимание, что инструменты, которые автоматически извлекают
JSDoc (например,
JsDossier) часто
игнорируют форматирование обычного текста, так что если вы сделаете так:

/**
 * Вычисляет вес на основе трех факторов:
 *   элементов отправлено
 *   элементов получено
 *   последняя временная метка
 */

это станет таким:

Вычисляет вес на основе трех факторов: элементов отправлено элементов получено последная временная метка

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

/**
 * Вычисляет вес на основе трех факторов:
 *  - элементов отправлено
 *  - элементов получено
 *  - последняя временная метка
 */

7.3 JSDoc теги

Стиль Google разрешает подмножество JSDoc-тегов. См.
?? для полного списка.
Большинство тегов должны занимать свою собственную строку, с тегом в
начале строки.

Запрещено:

/**
 * Тэг "param" должен занимать свою собственную строку и не может комбинироваться.
 * @param {number} left @param {number} right
 */
function add(left, right) { ... }

Простые теги, не требующие дополнительных данных (такие, как
@private, @const, @final,
@export), могут быть объединены в одну строку вместе с
дополнительным типом, если это необходимо.

/**
 * Размещайте более сложные аннотации (например, "implements" и "template").
 * на своих отдельных линиях.  Несколько простых тегов (например, "export" и "final").
 * могут быть скомбинированы в одной линии.
 * @export @final
 * @implements {Iterable<TYPE>}
 * @template TYPE
 */
class MyClass {
  /**
   * @param {!ObjType} obj Отдельный объект.
   * @param {number=} num Опциональное число.
   */
  constructor(obj, num = 42) {
    /** @private @const {!Array<!ObjType|number>} */
    this.data_ = [obj, num];
  }
}

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

Общую информацию об аннотировании типов в JavaScript см. в разделе
Аннотирование JavaScript для Closure Compiler
и
Типы в Closure Type System.

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

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

/**
 * Иллюстрирует перенос строки для длинных описаний.
 * @param {string} foo Это параметр с описанием, слишком длинным, чтобы 
 *     быть в одной строке.
 * @return {number} Это параметр с описанием, слишком длинным, чтобы 
 *     быть в одной строке.
 */
exports.method = function(foo) {
  return 5;
};

Не делайте отступы при переносе описания @desc или
@fileoverview.

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

Пример:

/**
 * @fileoverview Описание файла, цели его использование и информация
 * о его зависимостях.
 * @package
 */

Классы, интерфейсы и записи должны быть задокументированы описанием и
любыми параметрами шаблона, реализованными интерфейсами, видимостью или
другими соответствующими тегами. Описание класса должно давать читателю
достаточно информации, чтобы знать, как и когда использовать класс, а
также любые дополнительные моменты, необходимые для корректного
использования класса. Текстовые описания могут быть опущены в
конструкторе. @constructor и
@extends аннотации не используются с ключевым словом
class, если только класс не используется для объявления
@interface или не расширяет общий класс.

/**
 * Особенная цель события, которая делает полезные вещи.
 * @implements {Iterable<string>}
 */
class MyFancyTarget extends EventTarget {
  /**
   * @param {string} arg1 Аргумент, который делает это более полезным.
   * @param {!Array<number>} arg2 Список чисел для обработки.
   */
  constructor(arg1, arg2) {
    // ...
  }
};

/**
 * Записи также полезны.
 * @extends {Iterator<TYPE>}
 * @record
 * @template TYPE
 */
class Listable {
  /** @return {TYPE} Следующий элемент в строке, который будет возвращен. */
  next() {}
}

7.7 Комментарии перечисления и typedef

Все перечисления и typedefs должны быть задокументированы с помощью
соответствующих тегов JSDoc (@typedef или
@enum) на предшествующей строке. Публичные перечисления и
typedefs также должны иметь описание. Отдельные элементы перечисления
могут быть задокументированы JSDoc комментарием на предшествующей
строке.

/**
 * Полезное объединение типа, которое часто используется повторно.
 * @typedef {!Bandersnatch|!BandersnatchType}
 */
let CoolUnionType;


/**
 * Типы бандерснатчей.
 * @enum {string}
 */
const BandersnatchType = {
  /** Этот вид действительно странный */
  FRUMIOUS: 'frumious',
  /** Менее важный тип */
  MANXOME: 'manxome',
};

Typedefs полезны для определения типов коротких записей или псевдонимов
для объединений, сложных функций или общих типов. Следует избегать
typedefs’ов для типов записей с большим количеством полей, поскольку они
не позволяют документировать отдельные поля, а также использовать
шаблоны или рекурсивные ссылки. Для больших типов записей предпочитайте
@record.

7.8 Комментарии методов и функций

В методах и именованных функциях должны документироваться типы
параметров и возвращаемого значения, за исключением случая одинаковой
подписи
@override, где опущены все типы. Тип
this должен быть документирован при необходимости.
Возвращаемый тип может быть опущен, если функция не имеет непустых
выражений return.

Описания метода, параметров и возвращаемых значений (но не типов) могут
быть опущены, если они очевидны из остальной части JSDoc’a метода или из
его определения.

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

Если метод переопределяет метод суперкласса, он должен включать в себя
примечание @override. Переопределенные методы наследуют все
аннотации JSDoc от метода суперкласса (включая аннотации видимости) и
должны быть опущены в переопределенном методе. Однако, если в аннотациях
типа какой-либо тип уточнен, все @param и
@return аннотации должны быть указаны явно.

/** Класс, который что-то делает. */
class SomeClass extends SomeBaseClass {
  /**
   * Взаимодействует с экземпляром MyClass и что-то возвращает.
   * @param {!MyClass} obj Объект, который по какой-то причине требует детального
   *     описания, которое занимает несколько строк.
   * @param {!OtherClass} obviousOtherClass
   * @return {boolean} Если что-то произошло.
   */
  someMethod(obj, obviousOtherClass) { ... }

  /** @override */
  overriddenMethod(param) { ... }
}

/**
 * Демонстрирует, как функции верхнего уровня следуют тем же правилам. Данная функция
 * создает массив.
 * @param {TYPE} arg
 * @return {!Array<TYPE>}
 * @template TYPE
 */
function makeArray(arg) { ... }

Если необходимо только документировать параметры и возвращаемые типы
функции, можно дополнительно использовать встроенные JSDocs в сигнатуре
функции. Эти встроенные JSDoc’и определяют типы возвращаемого значения и
параметров без тегов.

function /** string */ foo(/** number */ arg) {...}

Если вам нужно описание или теги, используйте один JSDoc комментарий над
методом. Например, методы, возвращающие значения, должны иметь тег
@return.

class MyClass {
  /**
   * @param {number} arg
   * @return {string}
   */
  bar(arg) {...}
}
// Недопустимый однострочный JSDoc.

class MyClass {
  /** @return {string} */ foo() {...}
}

/** Function description. */ bar() {...}

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

promise.then(
    /** @return {string} */
    (/** !Array<string> */ items) => {
      doSomethingWith(items);
      return items[0];
    });

Для функциональных выражений, см.
??.

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

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

/** My class. */
class MyClass {
  /** @param {string=} someString */
  constructor(someString = 'default string') {
    /** @private @const {string} */
    this.someString_ = someString;

    /** @private @const {!OtherType} */
    this.someOtherThing_ = functionThatReturnsAThing();

    /**
     * Максимальное количество вещей на панели.
     * @type {number}
     */
    this.someProperty = 4;
  }
}

/**
 * Сколько раз мы попробуем, прежде чем прекратить.
 * @const {number}
 */
MyClass.RETRY_COUNT = 33;

7.10 Аннотации типа

Аннотации типов находятся в тегах @param,
@return, @this и @type, и
опционально в тегах @const, @export и на любых
тегах видимости. Аннотации типа, прикрепленные к тегам JSDoc, всегда
должны быть заключены в фигурные скобки.

7.10.1 Nullability

Система типов определяет модификаторы ! и
? для ненулевых и нулевых соответственно. Эти модификаторы
должны предшествовать типу.

Модификаторы Nullability имеют различные требования к различным типам,
которые делятся на две категории:

  1. Аннотации типов для примитивов (string,
    number, boolean, symbol,
    undefined, null) и литералов ({function(...): ...}
    и {{foo: string...}}) всегда ненулевые по умолчанию.
    Используйте модификатор ?, чтобы сделать его нулевым, но
    опускайте ненужный !.
  2. Ссылочные типы (обычно все, что угодно в
    ВверхнемВерблюжьемСтиле, включая
    some.namespace.ReferenceType) относятся к классу,
    перечислению, записи или typedef, определенному в другом месте. Так
    как эти типы могут быть или не быть нулевыми, невозможно определить по
    одному только имени, является ли оно нулевым или нет. Всегда
    используйте явные модификаторы ? и ! для
    этих типов, чтобы предотвратить двусмысленность при использовании в
    конкретных местах.

Плохо:

const /** MyObject */ myObject = null; // Непримитивные типы должны быть аннотированы.
const /** !number */ someNum = 5; // Примитивы по умолчанию не могут быть нулевыми.
const /** number? */ someNullableNum = null; // ? должно предшествовать типу.
const /** !{foo: string, bar: number} */ record = ...; // Всегда ненулевой.
const /** MyTypeDef */ def = ...; // Не ясно, что MyTypeDef нулевой.

// Не уверен, что объект (может быть нулевым), перечисление (ненулевой, если не указано иначе)
// или typedef (зависит от определения).
const /** SomeCamelCaseName */ n = ...;

Хорошо:

const /** ?MyObject */ myObject = null;
const /** number */ someNum = 5;
const /** ?number */ someNullableNum = null;
const /** {foo: string, bar: number} */ record = ...;
const /** !MyTypeDef */ def = ...;
const /** ?SomeCamelCaseName */ n = ...;

7.10.2 Преобразования типов

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

/** @type {number} */ (x)

7.10.3 Типы параметров шаблона

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

Плохо:

const /** !Object */ users = {};
const /** !Array */ books = [];
const /** !Promise */ response = ...;

Хорошо:

const /** !Object<string, !User> */ users = {};
const /** !Array<string> */ books = [];
const /** !Promise<!Response> */ response = ...;

const /** !Promise<undefined> */ thisPromiseReturnsNothingButParameterIsStillUseful = ...;
const /** !Object<string, *> */ mapOfEverything = {};

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

  • Object используется для иерархии типов, а не структура
    подобная Map.

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

Терминологическое уточнение:
функциональные выражения относятся к аннотации типов функций с
ключевым словом function в аннотации (см. примеры ниже).

Там, где дано определение функции, не используйте функциональные
выражения. Указывайте типы параметров и возвращаемых функций с помощью
@param и @return, либо со строчными
аннотациями (см. ??).
Это включает анонимные функции и функции, определенные и назначенные в
const (где функция jsdoc появляется над всем выражением
назначения).

Функциональные выражения необходимы, например, внутри
@typedef, @param или @return.
Используйте его также для переменных или свойств типа функции, если они
не инициализируются сразу с определением функции.

  /** @private {function(string): string} */
  this.idGenerator_ = googFunctions.identity;

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

Плохо — ошибка типа, но предупреждение не выдается:

/** @param {function()} generateNumber */
function foo(generateNumber) {
  const /** number */ x = generateNumber();  // Здесь нет ошибки типа времени компиляции.
}

foo(() => 'точно не число');

Хорошо:

/**
 * @param {function(): *} inputFunction1 Может вернуть любой тип.
 * @param {function(): undefined} inputFunction2 Точно ничего не
 *      возвращает.
 * Внимание: тип возвращаемого значения `foo` подразумевает {undefined}.
 */
function foo(inputFunction1, inputFunction2) {...}

7.10.5 Пробел

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

Хорошо:

/** @type {function(string): number} */

/** @type {{foo: number, bar: number}} */

/** @type {number|string} */

/** @type {!Object<string, string>} */

/** @type {function(this: Object<string, string>, number): string} */

/**
 * @type {function(
 *     !SuperDuperReallyReallyLongTypedefThatForcesTheLineBreak,
 *     !OtherVeryLongTypedef): string}
 */

/**
 * @type {!SuperDuperReallyReallyLongTypedefThatForcesTheLineBreak|
 *     !OtherVeryLongTypedef}
 */

Плохо:

// Пробел должен быть только после двоеточия
/** @type {function(string) : number} */

// Поставьте пробелы после двоеточий и запятых
/** @type {{foo:number,bar:number}} */

// Не должно быть пробелов в объединениях типов
/** @type {number | string} */

7.11 Аннотации видимости

Аннотации видимости (@private, @package,
@protected) могут быть указаны в
@fileoverview или на любом экспортированном символе или
свойстве. Не указывайте видимость для локальных переменных, будь то
внутри функции или на верхнем уровне модуля. Все имена
@private должны заканчиваться подчеркиванием.

8 Политика

8.1 С проблемами, не указанными в Google Style: Будьте последовательны!

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

8.2 Предупреждения компилятора

8.2.1 Используйте стандартный набор предупреждений

Насколько это возможно, проекты должны использовать
--warning_level=VERBOSE.

8.2.2 Как работать с предупреждениями

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

После того, как вы увидели предупреждение, попробуйте следующие шаги:

  1. Исправьте его или временно поправьте.
    Сделайте попытку полностью устранить предупреждение или найдите другой
    способ решить проблему, который позволит полностью избежать предупреждения.
  2. В другом случае, определите, не ложная ли это тревога.
    Если вы убеждены, что предупреждение недействительно, а код на самом
    деле безопасен и корректен, добавьте комментарий, чтобы убедить
    читателя в этом и примените аннотацию @suppress.
  3. Иначе, оставьте TODO комментарий. Это
    последнее средство. Если вы делаете это, не подавляйте
    предупреждение. Предупреждение должно быть видимым до тех пор, пока
    оно не будет должным образом исправлено.

8.2.3 Подавляйте предупреждения в самом узком диапазоне

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

Пример

/** @suppress {uselessCode} Нераспознанная декларация 'use asm' */
function fn() {
  'use asm';
  return 0;
}

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

8.3 Устаревание

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

8.4 Код не в Google Style

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

8.4.1 Переформатируйте существующий код

При обновлении стиля существующего кода, следуйте данным рекомендациям:

  1. Необязательно изменять весь существующий код в соответствии с текущими
    рекомендациями по стилю. Переформатирование существующего кода
    является компромиссом между существованием старого формата и нового
    (code churn). Правила стиля меняются с течением времени, и такого рода
    подстройки для поддержания соответствия создают ненужные изменения.
    Однако, если в файл вносятся новые изменения, он должен быть в Google Style.
  2. Будьте осторожны, чтобы не допустить, чтобы исправление кода на новый
    стиль мешало фокусироваться на основном коде. Если вы обнаружили, что
    вносите много изменений в стиль, которые не являются критичными,
    делайте эти изменения в отдельных файлах или участках кода.

8.4.2 Новый добавленный код: используйте Google Style

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

При добавлении нового кода в файл, которые не поддерживает Google Style,
рекомендуется сначала переформатировать существующий код с учетом
рекомендации в ??.

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

8.5 Локальные правила стиля

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

8.6 Сгенерированный код: в основном без правил

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

9 Приложения

9.1 Ссылки на теги JSDoc

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

9.1.1 Аннотации типа и другие Closure Compiler аннотации

Документация для JSDoc, используемая компилятором, описана в
Аннотирование JavaScript для Closure Compiler
и
Типы в системе типов Closure.

9.1.2 Документирующие аннотации

В дополнение к JSDoc, описанному в
Аннотирование JavaScript для Closure Compiler
следующие теги являются общими и хорошо поддерживаются различными
инструментами генерации документации (такими как
JsDossier) для чисто
документационных целей.

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

9.1.2.1 @author or @owner
Не рекомендуется.

Не рекомендуется.

Синтаксис: @author username@google.com (First Last).

/**
 * @fileoverview Утилиты для работы с текстами.
 * @author kuth@google.com (Uthur Pendragon)
 */

Документирование автора файла или владельца тестов обычно
используются только в комментарии @fileoverview. Тэг
@owner используется инструментальной панелью модульного
теста для определения того, кому принадлежат результаты тестов.

9.1.2.2 @bug

Синтаксис: @bug номер бага.

/** @bug 1234567 */
function testSomething() {
  // …
}

/**
 * @bug 1234568
 * @bug 1234569
 */
function testTwoBugs() {
  // …
}

Показывает, какие есть ошибки в тестах данной функции.

Если ошибок несколько, они должны иметь свою строку @bug, чтобы
поиск регрессионных тестов был максимально простым.

9.1.2.3 @codeУстарело. Не используйте.

Устарело. Не используйте. Вместо этого используйте обратные ссылки
Markdown.

Синтаксис: {@code ...}

Исторически `BatchItem` был написан как
{@code BatchItem}.

/** Обработка ожидаемого экземпляра `BatchItem`. */
function processBatchItems() {}

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

9.1.2.4 @desc

Синтаксис: @desc сообщения.

/** @desc Уведомление пользователя о том, что его учетная запись была создана. */
exports.MSG_ACCOUNT_CREATED = goog.getMsg(
    'Ваша учетная запись была успешно создана.');
9.1.2.5 @link

Синтаксис: {@link ...}

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

/** Обработка ожидаемого экземпляра {@link BatchItem}. */
function processBatchItems() {}

Историческое примечание: @link теги также
используются для создания внешних ссылок в генерируемой документации.
Для внешних ссылок всегда используйте синтаксис ссылки Markdown:

/**
 * Этот класс реализует полезное подмножество
 * [нативных интерфейсов Event](https://dom.spec.whatwg.org/#event).
 */
class ApplicationEvent {}
9.1.2.6 @see

Синтаксис: @see Link.

/**
 * Добавляет один элемент в небезопасном режиме.
 * @see #addSafely
 * @see goog.Collect
 * @see goog.RecklessAdder#add
 */

Ссылка на поиск другой функции или метода класса.

9.1.2.7 @supported Описание

Синтаксис: @supported Описание.

/**
 * @fileoverview Event Manager
 * Предоставляет абстрактный интерфейс к системам событий браузеров.
 * @supported IE10+, Chrome, Safari
 */

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

9.1.3 Специальные аннотации для фреймворков

Следующие аннотации относятся к конкретному фреймворку.

9.1.3.1 @ngInject for Angular 1
9.1.3.2 @polymerBehavior for Polymer

https://github.com/google/closure-compiler/wiki/Polymer-Pass.

9.1.4 Примечания к стандартным аннотациям Closure Compiler

Раньше следующие теги были стандартными, но теперь они устарели.

9.1.4.1 @exposeУстарел. Не используйте.

Устарел. Не используйте. Используйте вместо этого
@export и/или @nocollapse.

9.1.4.2 @inheritDocУстарел. Не используйте.

Устарел. Не используйте. Используйте вместо этого
@override
.

9.2 Правила стиля, вызывающие недопонимание

Вот коллекция менее известных или часто непонятных фактов о Google Style
для JavaScript. (Следующие утверждения верны; причем эти случаи не выдуманы).

  • Ни заявление об авторских правах, ни @author не требуется
    в исходном файле. (Также явно не рекомендуется)
  • Не существует однозначного и точного правила, регулирующего порядок членов
    класса (??).
  • Пустые блоки обычно могут быть представлены кратко как
    {}, как подробно описано в (??).
  • Главным указанием для переноса строк является: предпочтительнее
    переносить строки на более высоком синтаксическом уровне (??).
  • Допускаются не-ASCII-символы в строковых литералах, комментариях и
    JSDoc, и фактически рекомендуется, когда они делают код более легким
    для чтения, чем эквивалентный Unicode с экранированием (??).

Для поддержки различных аспектов Google Style существуют следующие
инструменты.

9.3.1 Closure Compiler

Эта программа выполняет проверку типа и другие проверки, оптимизации и
другие преобразования (такие, как даунгрейд кода с ECMAScript 6 на
ECMAScript 5).

9.3.2 clang-format

Данная программа переформатирует исходный код JavaScript в Google Style,
а также применяет ряд необязательных, но часто повышающих читабельность практик
форматирования. Вывод, полученный с помощью clang-format,
соответствует руководству по стилю.

clang-format не является обязательным. Авторам разрешено изменять его
вывод, а рецензентам — запрашивать вносить дополнения; споры разрешаются
обычным образом. Также могут приниматься решения о локальном внедрении
новых изменений.

9.3.3 Линтер Closure Compiler

Данная программа проверяет на наличие различных ошибок и антишаблонов.

9.3.4 Фреймворк Conformance

JS Conformance Framework — это инструмент, входящий в состав Closure
Compiler, который предоставляет разработчикам простое средство для
задания набора дополнительных проверок, которые будут выполняться в их
коде перед стандартными проверками. Проверки на соответствие могут,
например, запретить доступ к определенному свойству, вызов определенной
функции или недостающую информацию о типе.

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

Для получения дополнительной информации обратитесь к официальной
документации
JS Conformance Framework.

9.4 Исключение устаревших платформ

9.4.1 Описание

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

  • Использование декларации var допустимо
  • Использование декларации arguments допустимо
  • Опциональные параметры без стандартных значений допустимы

9.4.2 Использование var

9.4.2.1 var имеет не блочную область видимости

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

for (var i = 0; i < 3; ++i) {
  var iteration = i;
  setTimeout(function() { console.log(iteration); }, i*1000);
}

// лог 2, 2, 2 -- а не 0, 1, 2
// потому что `iteration` имеет область видимости в пределах функции, а не локально для цикла.

9.4.2.2 Объявляйте переменные как можно ближе к месту первого использования

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

function sillyFunction() {
  var count = 0;
  for (var x in y) {
    // "count" может быть объявлена здесь, но не делайте так
    count++;
  }
  console.log(count + ' элементов в y');
}
9.4.2.3 Использование @const для константных значений

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

9.4.3 Не используйте объявления функций с видимостью в пределах блока

Не делайте так:

if (x) {
  function foo() {}
}

Хотя большинство виртуальных машин на JavaScript, реализованных до
ECMAScript 6, поддерживают функциональные декларации внутри блоков, они
не были стандартизированы. Введения были несовместимы друг с другом и
теперь также со стандартным поведением ECMAScript 6 при объявлении функций в
блоках. ECMAScript 5 и ранее допускал только объявления функций в списке
корневых операторов скрипта или функции и явно запрещал их в блочных
диапазонах в строгом режиме.

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

if (x) {
  var foo = function() {};
}

9.4.4 Управление зависимостями при помощи
goog.provide/goog.require

9.4.4.1 Резюме

Предупреждение: управление зависимостями с помощью
goog.provide устарело.

Все новые файлы, даже в проектах, использующих
goog.provide для старых файлов, должны использовать
goog.module. Следующие правила предназначены только для уже существующих
goog.provide файлов.

  • Размещайте все goog.provide в самом начале файла, после —
    goog.require
    . Разделяйте provide от require с помощью пустой линии.
  • Сортируйте записи в алфавитном порядке (сначала заглавными буквами).
  • Не переносите оператор goog.provide и
    goog.require. Превышайте лимит в 80 символов.
  • Используйте только provide для символов высшего порядка.

goog.provide операторы должны быть сгруппированы и помещены
первыми. Все утверждения goog.require должны следовать за
ними. Оба списка должны быть разделены пустой строкой.

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

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

goog.provide('namespace.MyClass');
goog.provide('namespace.helperFoo');

goog.require('an.extremelyLongNamespace.thatSomeoneThought.wouldBeNice.andNowItIsLonger.Than80Columns');
goog.require('goog.dom');
goog.require('goog.dom.TagName');
goog.require('goog.dom.classes');
goog.require('goog.dominoes');

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

Делайте так:

goog.provide('namespace.MyClass');

А не:

goog.provide('namespace.MyClass');
goog.provide('namespace.MyClass.CONSTANT');
goog.provide('namespace.MyClass.Enum');
goog.provide('namespace.MyClass.InnerClass');
goog.provide('namespace.MyClass.TypeDef');
goog.provide('namespace.MyClass.staticMethod');

Могут также предоставляться (provide) члены в пространствах имен:

goog.provide('foo.bar');
goog.provide('foo.bar.CONSTANT');
goog.provide('foo.bar.method');
9.4.4.2 Использование псевдонимов с goog.scope

Предупреждение: goog.scope устарел.
Новые файлы не должны использовать goog.scope даже в
проектах с существующим использованием goog.scope.

goog.scope может быть использован для сокращения ссылок на
символы, расположенных в пространстве имен кода с использованием
управления зависимостями при помощи
goog.provide/goog.require.

В каждый файл может быть добавлен только один вызов
goog.scope. Всегда помещайте его в глобальную область
видимости.

Вызову goog.scope(function()) { должна предшествовать ровно
одна пустая строка и следовать после операторов
goog.provide, goog.require или комментариев
верхнего уровня. Вызов должен быть закрыт в последней строке файла.
Добавьте // goog.scope к закрывающему выражению области
видимости. Отделите комментарий от точки с запятой двумя пробелами.

Аналогично пространствам имен в C++, не отступайте под объявлениями
goog.scope. Вместо этого продолжайте без отступа.

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

goog.scope(function() {
var Button = goog.ui.Button;

Button = function() { ... };
...

Имена должны быть такими же, как и последнее свойство глобального
объекта, на который они накладывают псевдонимы.

goog.provide('my.module.SomeType');

goog.require('goog.dom');
goog.require('goog.ui.Button');

goog.scope(function() {
var Button = goog.ui.Button;
var dom = goog.dom;

// Псевдоним нового типа после декларации конструктора.
my.module.SomeType = function() { ... };
var SomeType = my.module.SomeType;

// Объявляйте методы в прототипе, как обычно:
SomeType.prototype.findButton = function() {
  // Кнопка с псевдонимом как выше.
  this.button = new Button(dom.getElement('my-button'));
};
...
});  // goog.scope
9.4.4.3 goog.forwardDeclare

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

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

Оператор goog.forwardDeclare должен следовать тем же
правилам стиля, что и goog.require и
goog.requireType. Весь блок
goog.forwardDeclare, goog.require и
goog.requireType операторов должен сортироваться в
алфавитном порядке.

  • Overview / Web Technology

    Web technology reference for developers

  • HTML

    Structure of content on the web

  • CSS

    Code used to describe document style

  • JavaScript

    General-purpose scripting language

  • HTTP

    Protocol for transmitting web resources

  • Web APIs

    Interfaces for building web applications

  • Web Extensions

    Developing extensions for web browsers

  • Web Technology

    Web technology reference for developers

  • The syntax of JavaScript is the set of rules that define a correctly structured JavaScript program.

    The examples below make use of the log function of the console object present in most browsers for standard text output.

    The JavaScript standard library lacks an official standard text output function (with the exception of document.write). Given that JavaScript is mainly used for client-side scripting within modern web browsers, and that almost all Web browsers provide the alert function, alert can also be used, but is not commonly used.

    Origins[edit]

    Brendan Eich summarized the ancestry of the syntax in the first paragraph of the JavaScript 1.1 specification[1][2] as follows:

    JavaScript borrows most of its syntax from Java, but also inherits from Awk and Perl, with some indirect influence from Self in its object prototype system.

    Basics[edit]

    Case sensitivity[edit]

    JavaScript is case sensitive. It is common to start the name of a constructor with a capitalised letter, and the name of a function or variable with a lower-case letter.

    Example:

    var a = 5;
    console.log(a); // 5
    console.log(A); // throws a ReferenceError: A is not defined
    

    Whitespace and semicolons[edit]

    Unlike in C, whitespace in JavaScript source can directly impact semantics. Semicolons end statements in JavaScript. Because of automatic semicolon insertion (ASI), some statements that are well formed when a newline is parsed will be considered complete, as if a semicolon were inserted just prior to the newline. Some authorities advise supplying statement-terminating semicolons explicitly, because it may lessen unintended effects of the automatic semicolon insertion.[3]

    There are two issues: five tokens can either begin a statement or be the extension of a complete statement; and five restricted productions, where line breaks are not allowed in certain positions, potentially yielding incorrect parsing.

    The five problematic tokens are the open parenthesis «(«, open bracket «[«, slash «/«, plus «+«, and minus «-«. Of these, the open parenthesis is common in the immediately-invoked function expression pattern, and open bracket occurs sometimes, while others are quite rare. An example:

    a = b + c
    (d + e).foo()
    
    // Treated as:
    //  a = b + c(d + e).foo();
    

    with the suggestion that the preceding statement be terminated with a semicolon.

    Some suggest instead the use of leading semicolons on lines starting with ‘(‘ or ‘[‘, so the line is not accidentally joined with the previous one. This is known as a defensive semicolon, and is particularly recommended, because code may otherwise become ambiguous when it is rearranged. For example:

    a = b + c
    ;(d + e).foo()
    
    // Treated as:
    //  a = b + c;
    //  (d + e).foo();
    

    Initial semicolons are also sometimes used at the start of JavaScript libraries, in case they are appended to another library that omits a trailing semicolon, as this can result in ambiguity of the initial statement.

    The five restricted productions are return, throw, break, continue, and post-increment/decrement. In all cases, inserting semicolons does not fix the problem, but makes the parsed syntax clear, making the error easier to detect. return and throw take an optional value, while break and continue take an optional label. In all cases, the advice is to keep the value or label on the same line as the statement. This most often shows up in the return statement, where one might return a large object literal, which might be accidentally placed starting on a new line. For post-increment/decrement, there is potential ambiguity with pre-increment/decrement, and again it is recommended to simply keep these on the same line.

    return
    a + b;
    
    // Returns undefined. Treated as:
    //   return;
    //   a + b;
    // Should be written as:
    //   return a + b;
    

    [edit]

    Comment syntax is the same as in C++, Swift and many other languages.

    // a short, one-line comment
    
    /* this is a long, multi-line comment
      about my script. May it one day
      be great. */
    
    /* Comments /* may not be nested */ Syntax error */
    

    Variables[edit]

    Variables in standard JavaScript have no type attached, so any value (each value has a type) can be stored in any variable. Starting with ES6, the 6th version of the language, variables could be declared with var for function scoped variables, and let or const which are for block level variables. Before ES6, variables could only be declared with a var statement. Values assigned to variables declared with const cannot be changed, but its properties can. A variable’s identifier must start with a letter, underscore (_), or dollar sign ($), while subsequent characters can also be digits (0-9). JavaScript is case sensitive, so the uppercase characters «A» through «Z» are different from the lowercase characters «a» through «z».

    Starting with JavaScript 1.5, ISO 8859-1 or Unicode letters (or uXXXX Unicode escape sequences) can be used in identifiers.[4] In certain JavaScript implementations, the at sign (@) can be used in an identifier, but this is contrary to the specifications and not supported in newer implementations.[citation needed]

    Scoping and hoisting[edit]

    Variables declared with var are lexically scoped at a function level, while ones with let or const have a block level scope. Since declarations are processed before any code is executed, a variable can be assigned to and used prior to being declared in the code.[5] This is referred to as hoisting, and it is equivalent to variables being forward declared at the top of the function or block.[6]

    With var, let, and const statements, only the declaration is hoisted; assignments are not hoisted. Thus a var x = 1 statement in the middle of the function is equivalent to a var x declaration statement at the top of the function, and an x = 1 assignment statement at that point in the middle of the function. This means that values cannot be accessed before they are declared; forward reference is not possible. With var a variable’s value is undefined until it is initialized. Variables declared with let or const cannot be accessed until they have been initialized, so referencing such variables before will cause an error.

    Function declarations, which declare a variable and assign a function to it, are similar to variable statements, but in addition to hoisting the declaration, they also hoist the assignment – as if the entire statement appeared at the top of the containing function – and thus forward reference is also possible: the location of a function statement within an enclosing function is irrelevant. This is different from a function expression being assigned to a variable in a var, let, or const statement.

    So, for example,

    var func = function() { .. } // declaration is hoisted only
    function func() { .. } // declaration and assignment are hoisted
    

    Block scoping can be produced by wrapping the entire block in a function and then executing it – this is known as the immediately-invoked function expression pattern – or by declaring the variable using the let keyword.

    Declaration and assignment[edit]

    Variables declared outside a scope are global. If a variable is declared in a higher scope, it can be accessed by child scopes.

    When JavaScript tries to resolve an identifier, it looks in the local scope. If this identifier is not found, it looks in the next outer scope, and so on along the scope chain until it reaches the global scope where global variables reside. If it is still not found, JavaScript will raise a ReferenceError exception.

    When assigning an identifier, JavaScript goes through exactly the same process to retrieve this identifier, except that if it is not found in the global scope, it will create the «variable» in the scope where it was created.[7] As a consequence, a variable never declared will be global, if assigned. Declaring a variable (with the keyword var) in the global scope (i.e. outside of any function body (or block in the case of let/const)), assigning a never declared identifier or adding a property to the global object (usually window) will also create a new global variable.

    Note that JavaScript’s strict mode forbids the assignment of an undeclared variable, which avoids global namespace pollution.

    Examples[edit]

    Here are some examples of variable declarations and scope:

    var x1 = 0; // A global variable, because it is not in any function
    let x2 = 0; // Also global, this time because it is not in any block
    
    function f() {
      var z = 'foxes', r = 'birds'; // 2 local variables
      m = 'fish'; // global, because it wasn't declared anywhere before
    
      function child() {
        var r = 'monkeys'; // This variable is local and does not affect the "birds" r of the parent function.
        z = 'penguins'; // Closure: Child function is able to access the variables of the parent function.
      }
    
      twenty = 20; // This variable is declared on the next line, but usable anywhere in the function, even before, as here
      var twenty;
    
      child();
      return x1 + x2; // We can use x1 and x2 here, because they are global
    }
    
    f();
    
    console.log(z); // This line will raise a ReferenceError exception, because the value of z is no longer available
    
    for (let i = 0; i < 10; i++) console.log(i);
    console.log(i); // throws a ReferenceError: i is not defined
    
    for (const i = 0; i < 10; i++) console.log(i); // throws a TypeError: Assignment to constant variable
    
    for (const i of [1,2,3]) console.log(i); //will not raise an exception. i is not reassigned but recreated in every iteration
    
    const pi; // throws a SyntaxError: Missing initializer in const declaration
    

    Primitive data types[edit]

    The JavaScript language provides six primitive data types:

    • Undefined
    • Number
    • BigInt
    • String
    • Boolean
    • Symbol

    Some of the primitive data types also provide a set of named values that represent the extents of the type boundaries. These named values are described within the appropriate sections below.

    Undefined[edit]

    The value of «undefined» is assigned to all uninitialized variables, and is also returned when checking for object properties that do not exist. In a Boolean context, the undefined value is considered a false value.

    Note: undefined is considered a genuine primitive type. Unless explicitly converted, the undefined value may behave unexpectedly in comparison to other types that evaluate to false in a logical context.

    var test;                         // variable declared, but not defined, ...
                                      // ... set to value of undefined
    var testObj = {};
    console.log(test);                // test variable exists, but value not ...
                                      // ... defined, displays undefined
    console.log(testObj.myProp);      // testObj exists, property does not, ...
                                      // ... displays undefined
    console.log(undefined == null);   // unenforced type during check, displays true
    console.log(undefined === null);  // enforce type during check, displays false
    

    Note: There is no built-in language literal for undefined. Thus (x === undefined) is not a foolproof way to check whether a variable is undefined, because in versions before ECMAScript 5, it is legal for someone to write var undefined = "I'm defined now";. A more robust approach is to compare using (typeof x === 'undefined').

    Functions like this won’t work as expected:

    function isUndefined(x) { var u; return x === u; }             // like this...
    function isUndefined(x) { return x === void 0; }               // ... or that second one
    function isUndefined(x) { return (typeof x) === "undefined"; } // ... or that third one
    

    Here, calling isUndefined(my_var) raises a ReferenceError if my_var is an unknown identifier, whereas typeof my_var === 'undefined' doesn’t.

    Number[edit]

    Numbers are represented in binary as IEEE-754 floating point doubles. Although this format provides an accuracy of nearly 16 significant digits, it cannot always exactly represent real numbers, including fractions.

    This becomes an issue when comparing or formatting numbers. For example:

    console.log(0.2 + 0.1 === 0.3); // displays false
    console.log(0.94 - 0.01);       // displays 0.9299999999999999
    

    As a result, a routine such as the toFixed() method should be used to round numbers whenever they are formatted for output.

    Numbers may be specified in any of these notations:

    345;    // an "integer", although there is only one numeric type in JavaScript
    34.5;   // a floating-point number
    3.45e2; // another floating-point, equivalent to 345
    0b1011; // a binary integer equal to 11
    0o377;   // an octal integer equal to 255
    0xFF;   // a hexadecimal integer equal to 255, digits represented by the ...
            // ... letters A-F may be upper or lowercase
    

    There’s also a numeric separator, _ (the underscore), introduced in ES2021:

    // Note: Wikipedia syntax doesn't support numeric separators yet
    1_000_000_000;    // Used with big numbers
    1_000_000.5;      // Support with decimals
    1_000e1_000;      // Support with exponents
    
    // Support with binary, octals and hex
    0b0000_0000_0101_1011;
    0o0001_3520_0237_1327;
    0xFFFF_FFFF_FFFF_FFFE;
    
    // But you can't use them next to a non-digit number part, or at the start or end
    _12; // Variable is not defined (the underscore makes it a variable identifier)
    12_; // Syntax error (cannot be at the end of numbers)
    12_.0; // Syntax error (doesn't make sense to put a separator next to the decimal point)
    12._0; // Syntax error
    12e_6; // Syntax error (next to "e", a non-digit. Doesn't make sense to put a separator at the start)
    1000____0000; // Syntax error (next to "_", a non-digit. Only 1 separator at a time is allowed
    

    The extents +∞, −∞ and NaN (Not a Number) of the number type may be obtained by two program expressions:

    Infinity; // positive infinity (negative obtained with -Infinity for instance)
    NaN;      // The Not-A-Number value, also returned as a failure in ...
              // ... string-to-number conversions
    

    Infinity and NaN are numbers:

    typeof Infinity;   // returns "number"
    typeof NaN;        // returns "number"
    

    These three special values correspond and behave as the IEEE-754 describes them.

    The Number constructor (used as a function), or a unary + or -, may be used to perform explicit numeric conversion:

    var myString = "123.456";
    var myNumber1 = Number(myString);
    var myNumber2 = +myString;
    

    When used as a constructor, a numeric wrapper object is created (though it is of little use):

    myNumericWrapper = new Number(123.456);
    

    However, NaN is not equal to itself:

    const nan = NaN;
    console.log(NaN == NaN);        // false
    console.log(NaN === NaN);       // false
    console.log(NaN !== NaN);       // true
    console.log(nan !== nan);       // true
    
    // You can use the isNaN methods to check for NaN
    console.log(isNaN("converted to NaN"));     // true
    console.log(isNaN(NaN));                    // true
    console.log(Number.isNaN("not converted")); // false
    console.log(Number.isNaN(NaN));             // true
    

    BigInt[edit]

    BigInts can be used for arbitrarily large integers. Especially whole numbers larger than 253 — 1, which is the largest number JavaScript can reliably represent with the Number primitive and represented by the Number.MAX_SAFE_INTEGER constant.

    When dividing BigInts, the results are truncated.

    String[edit]

    A string in JavaScript is a sequence of characters. In JavaScript, strings can be created directly (as literals) by placing the series of characters between double («) or single (‘) quotes. Such strings must be written on a single line, but may include escaped newline characters (such as n). The JavaScript standard allows the backquote character (`, a.k.a. grave accent or backtick) to quote multiline literal strings, but this is supported only on certain browsers as of 2016: Firefox and Chrome, but not Internet Explorer 11.[8]

    var greeting = "Hello, World!";
    var anotherGreeting = 'Greetings, people of Earth.';
    

    Individual characters within a string can be accessed using the charAt method (provided by String.prototype). This is the preferred way when accessing individual characters within a string, because it also works in non-modern browsers:

    var h = greeting.charAt(0);
    

    In modern browsers, individual characters within a string can be accessed (as strings with only a single character) through the same notation as arrays:

    However, JavaScript strings are immutable:

    greeting[0] = "H"; // Fails.
    

    Applying the equality operator («==») to two strings returns true, if the strings have the same contents, which means: of the same length and containing the same sequence of characters (case is significant for alphabets). Thus:

    var x = "World";
    var compare1 = ("Hello, " +x == "Hello, World"); // Here compare1 contains true.
    var compare2 = ("Hello, " +x == "hello, World"); // Here compare2 contains  ...
                                                     // ... false since the ...
                                                     // ... first characters ...
                                                     // ... of both operands ...
                                                     // ... are not of the same case.
    

    Quotes of the same type cannot be nested unless they are escaped.

    var x = '"Hello, World!" he said.'; // Just fine.
    var x = ""Hello, World!" he said."; //  Not good.
    var x = ""Hello, World!" he said."; // Works by escaping " with "
    

    The String constructor creates a string object (an object wrapping a string):

    var greeting = new String("Hello, World!");
    

    These objects have a valueOf method returning the primitive string wrapped within them:

    var s = new String("Hello !");
    typeof s; // Is 'object'.
    typeof s.valueOf(); // Is 'string'.
    

    Equality between two String objects does not behave as with string primitives:

    var s1 = new String("Hello !");
    var s2 = new String("Hello !");
    s1 == s2; // Is false, because they are two distinct objects.
    s1.valueOf() == s2.valueOf(); // Is true.
    

    Boolean[edit]

    JavaScript provides a Boolean data type with true and false literals. The typeof operator returns the string «boolean» for these primitive types. When used in a logical context, 0, -0, null, NaN, undefined, and the empty string («») evaluate as false due to automatic type conversion. All other values (the complement of the previous list) evaluate as true, including the strings «0», «false» and any object.

    Type conversion[edit]

    Automatic type coercion by the equality comparison operators (== and !=) can be avoided by using the type checked comparison operators (=== and !==).

    When type conversion is required, JavaScript converts Boolean, Number, String, or Object operands as follows:[9]

    Number and String
    The string is converted to a number value. JavaScript attempts to convert the string numeric literal to a Number type value. First, a mathematical value is derived from the string numeric literal. Next, this value is rounded to nearest Number type value.
    Boolean
    If one of the operands is a Boolean, the Boolean operand is converted to 1 if it is true, or to 0 if it is false.
    Object
    If an object is compared with a number or string, JavaScript attempts to return the default value for the object. An object is converted to a primitive String or Number value, using the .valueOf() or .toString() methods of the object. If this fails, a runtime error is generated.


    Douglas Crockford advocates the terms «truthy» and «falsy» to describe how values of various types behave when evaluated in a logical context, especially in regard to edge cases.[10]
    The binary logical operators returned a Boolean value in early versions of JavaScript, but now they return one of the operands instead. The left–operand is returned, if it can be evaluated as : false, in the case of conjunction: (a && b), or true, in the case of disjunction: (a || b); otherwise the right–operand is returned. Automatic type coercion by the comparison operators may differ for cases of mixed Boolean and number-compatible operands (including strings that can be evaluated as a number, or objects that can be evaluated as such a string), because the Boolean operand will be compared as a numeric value. This may be unexpected. An expression can be explicitly cast to a Boolean primitive by doubling the logical negation operator: (!!), using the Boolean() function, or using the conditional operator: (c ? t : f).

    // Automatic type coercion
    console.log(true  ==   2 ); // false... true  → 1 !== 2 ←  2
    console.log(false ==   2 ); // false... false → 0 !== 2 ←  2
    console.log(true  ==   1 ); // true.... true  → 1 === 1 ←  1
    console.log(false ==   0 ); // true.... false → 0 === 0 ←  0
    console.log(true  ==  "2"); // false... true  → 1 !== 2 ← "2"
    console.log(false ==  "2"); // false... false → 0 !== 2 ← "2"
    console.log(true  ==  "1"); // true.... true  → 1 === 1 ← "1"
    console.log(false ==  "0"); // true.... false → 0 === 0 ← "0"
    console.log(false ==  "" ); // true.... false → 0 === 0 ← ""
    console.log(false ==  NaN); // false... false → 0 !== NaN
    
    console.log(NaN == NaN); // false...... NaN is not equivalent to anything, including NaN.
    
    // Type checked comparison (no conversion of types and values)
    console.log(true === 1); // false...... data types do not match
    
    // Explicit type coercion
    console.log(true === !!2);   // true.... data types and values match
    console.log(true === !!0);   // false... data types match, but values differ
    console.log( 1  ? true : false); // true.... only ±0 and NaN are "falsy" numbers
    console.log("0" ? true : false); // true.... only the empty string is "falsy"
    console.log(Boolean({}));    // true.... all objects are "truthy"
    

    The new operator can be used to create an object wrapper for a Boolean primitive. However, the typeof operator does not return boolean for the object wrapper, it returns object. Because all objects evaluate as true, a method such as .valueOf(), or .toString(), must be used to retrieve the wrapped value. For explicit coercion to the Boolean type, Mozilla recommends that the Boolean() function (without new) be used in preference to the Boolean object.

    var b = new Boolean(false);   // Object  false {}
    var t = Boolean(b);           // Boolean true
    var f = Boolean(b.valueOf()); // Boolean false
    var n = new Boolean(b);       // Not recommended
    n = new Boolean(b.valueOf()); // Preferred
    
    if (0 || -0 || "" || null || undefined || b.valueOf() || !new Boolean() || !t) {
      console.log("Never this");
    } else if ([] && {} && b && typeof b === "object" && b.toString() === "false") {
      console.log("Always this");
    }
    

    Symbol[edit]

    New in ECMAScript6. A Symbol is a unique and immutable identifier.

    Example:

    var x = Symbol(1);
    var y = Symbol(1);
    x === y; // => false
    
    var symbolObject = {};
    var normalObject = {};
    
    // since x and y are unique,
    // they can be used as unique keys in an object
    symbolObject[x] = 1;
    symbolObject[y] = 2;
    
    symbolObject[x]; // => 1
    symbolObject[y]; // => 2
    
    // as compared to normal numeric keys
    normalObject[1] = 1;
    normalObject[1] = 2; // overrides the value of 1
    
    normalObject[1]; // => 2
    
    // changing the value of x does not change the key stored in the object
    x = Symbol(3);
    symbolObject[x]; // => undefined
    
    // changing x back just creates another unique Symbol
    x = Symbol(1);
    symbolObject[x]; // => undefined
    

    There are also well known symbols.

    One of which is Symbol.iterator; if something implements Symbol.iterator, it’s iterable:

    let x = [1, 2, 3, 4]; // x is an Array
    x[Symbol.iterator] === Array.prototype[Symbol.iterator]; // and Arrays are iterable
    
    const xIterator = x[Symbol.iterator](); // The [Symbol.iterator] function should provide an iterator for x
    xIterator.next(); // { value: 1, done: false }
    xIterator.next(); // { value: 2, done: false }
    xIterator.next(); // { value: 3, done: false }
    xIterator.next(); // { value: 4, done: false }
    xIterator.next(); // { value: undefined, done: true }
    xIterator.next(); // { value: undefined, done: true }
    
    // for..of loops automatically iterate values
    for (const value of x) {
       console.log(value); // 1 2 3 4
    }
    
    // Sets are also iterable:
    [Symbol.iterator] in Set.prototype; // true
    
    for (const value of new Set(['apple', 'orange'])) {
       console.log(value); // "apple" "orange"
    }
    

    Native objects[edit]

    The JavaScript language provides a handful of native objects. JavaScript native objects are considered part of the JavaScript specification. JavaScript environment notwithstanding, this set of objects should always be available.

    Array[edit]

    An Array is a JavaScript object prototyped from the Array constructor specifically designed to store data values indexed by integer keys. Arrays, unlike the basic Object type, are prototyped with methods and properties to aid the programmer in routine tasks (for example, join, slice, and push).

    As in the C family, arrays use a zero-based indexing scheme: A value that is inserted into an empty array by means of the push method occupies the 0th index of the array.

    var myArray = [];            // Point the variable myArray to a newly ...
                                 // ... created, empty Array
    myArray.push("hello World"); // Fill the next empty index, in this case 0
    console.log(myArray[0]);           // Equivalent to console.log("hello World");
    

    Arrays have a length property that is guaranteed to always be larger than the largest integer index used in the array. It is automatically updated, if one creates a property with an even larger index. Writing a smaller number to the length property will remove larger indices.

    Elements of Arrays may be accessed using normal object property access notation:

    myArray[1];  // the 2nd item in myArray
    myArray["1"];
    

    The above two are equivalent. It’s not possible to use the «dot»-notation or strings with alternative representations of the number:

    myArray.1;     // syntax error
    myArray["01"]; // not the same as myArray[1]
    

    Declaration of an array can use either an Array literal or the Array constructor:

    let myArray;
    
    // Array literals
    myArray = [1, 2]; // length of 2
    myArray = [1, 2,]; // same array - You can also have an extra comma at the end
    
    // It's also possible to not fill in parts of the array
    myArray = [0, 1, /* hole */, /* hole */, 4, 5]; // length of 6
    myArray = [0, 1, /* hole */, /* hole */, 4, 5,]; // same array
    myArray = [0, 1, /* hole */, /* hole */, 4, 5, /* hole */,]; // length of 7
    
    // With the constructor
    myArray = new Array(0, 1, 2, 3, 4, 5); // length of 6
    myArray = new Array(365);              // an empty array with length 365
    

    Arrays are implemented so that only the defined elements use memory; they are «sparse arrays». Setting myArray[10] = 'someThing' and myArray[57] = 'somethingOther' only uses space for these two elements, just like any other object. The length of the array will still be reported as 58. The maximum length of an array is 4,294,967,295 which corresponds to 32-bit binary number (11111111111111111111111111111111)2.

    One can use the object declaration literal to create objects that behave much like associative arrays in other languages:

    dog = {color: "brown", size: "large"};
    dog["color"]; // results in "brown"
    dog.color;    // also results in "brown"
    

    One can use the object and array declaration literals to quickly create arrays that are associative, multidimensional, or both. (Technically, JavaScript does not support multidimensional arrays, but one can mimic them with arrays-of-arrays.)

    cats = [{color: "brown", size: "large"},
        {color: "black", size: "small"}];
    cats[0]["size"];      // results in "large"
    
    dogs = {rover: {color: "brown", size: "large"},
        spot: {color: "black", size: "small"}};
    dogs["spot"]["size"]; // results in "small"
    dogs.rover.color;     // results in "brown"
    

    Date[edit]

    A Date object stores a signed millisecond count with zero representing 1970-01-01 00:00:00 UT and a range of ±108 days. There are several ways of providing arguments to the Date constructor. Note that months are zero-based.

    new Date();                       // create a new Date instance representing the current time/date.
    new Date(2010, 2, 1);             // create a new Date instance representing 2010-Mar-01 00:00:00
    new Date(2010, 2, 1, 14, 25, 30); // create a new Date instance representing 2010-Mar-01 14:25:30
    new Date("2010-3-1 14:25:30");    // create a new Date instance from a String.
    

    Methods to extract fields are provided, as well as a useful toString:

    var d = new Date(2010, 2, 1, 14, 25, 30); // 2010-Mar-01 14:25:30;
    
    // Displays '2010-3-1 14:25:30':
    console.log(d.getFullYear() + '-' + (d.getMonth() + 1) + '-' + d.getDate() + ' '
        + d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds());
    
    // Built-in toString returns something like 'Mon Mar 01 2010 14:25:30 GMT-0500 (EST)':
    console.log(d);
    

    Error[edit]

    Custom error messages can be created using the Error class:

    throw new Error("Something went wrong.");
    

    These can be caught by try…catch…finally blocks as described in the section on exception handling.

    Math[edit]

    The Math object contains various math-related constants (for example, π) and functions (for example, cosine). (Note that the Math object has no constructor, unlike Array or Date. All its methods are «static», that is «class» methods.) All the trigonometric functions use angles expressed in radians, not degrees or grads.

    Some of the constants contained in the Math object

    Property Returned value
    rounded to 5 digits
    Description
    Math.E 2.7183 e: Natural logarithm base
    Math.LN2 0.69315 Natural logarithm of 2
    Math.LN10 2.3026 Natural logarithm of 10
    Math.LOG2E 1.4427 Logarithm to the base 2 of e
    Math.LOG10E 0.43429 Logarithm to the base 10 of e
    Math.PI 3.14159 π: circumference/diameter of a circle
    Math.SQRT1_2 0.70711 Square root of ½
    Math.SQRT2 1.4142 Square root of 2
    Methods of the Math object

    Example Returned value
    rounded to 5 digits
    Description
    Math.abs(-2.3) 2.3 Absolute value
    Math.acos(Math.SQRT1_2) 0.78540 rad = 45° Arccosine
    Math.asin(Math.SQRT1_2) 0.78540 rad = 45° Arcsine
    Math.atan(1) 0.78540 rad = 45° Half circle arctangent (-pi /2 to {displaystyle +pi /2})
    Math.atan2(-3.7, -3.7) −2.3562 rad = −135° Whole circle arctangent (-pi to {displaystyle +pi })
    Math.ceil(1.1) 2 Ceiling: round up to smallest integer ≥ argument
    Math.cos(Math.PI/4) 0.70711 Cosine
    Math.exp(1) 2.7183 Exponential function: e raised to this power
    Math.floor(1.9) 1 Floor: round down to largest integer ≤ argument
    Math.log(Math.E) 1 Natural logarithm, base e
    Math.max(1, -2) 1 Maximum: (x > y) ? x : y
    Math.min(1, -2) −2 Minimum: (x < y) ? x : y
    Math.pow(-3, 2) 9 Exponentiation (raised to the power of): Math.pow(x, y) gives xy
    Math.random() e.g. 0.17068 Pseudorandom number between 0 (inclusive) and 1 (exclusive)
    Math.round(1.5) 2 Round to the nearest integer; half fractions are rounded up (e.g. 1.5 rounds to 2)
    Math.sin(Math.PI/4) 0.70711 Sine
    Math.sqrt(49) 7 Square root
    Math.tan(Math.PI/4) 1 Tangent

    Regular expression[edit]

    /expression/.test(string);     // returns Boolean
    "string".search(/expression/); // returns position Number
    "string".replace(/expression/, replacement);
    
    // Here are some examples
    if (/Tom/.test("My name is Tom")) console.log("Hello Tom!");
    console.log("My name is Tom".search(/Tom/));          // == 11 (letters before Tom)
    console.log("My name is Tom".replace(/Tom/, "John")); // == "My name is John"
    

    Character classes[edit]

    // d  - digit
    // D  - non digit
    // s  - space
    // S  - non space
    // w  - word char
    // W  - non word
    // [ ] - one of
    // [^] - one not of
    //  -  - range
    
    if (/d/.test('0'))                   console.log('Digit');
    if (/[0-9]/.test('6'))                console.log('Digit');
    if (/[13579]/.test('1'))              console.log('Odd number');
    if (/SSsSSSS/.test('My name')) console.log('Format OK');
    if (/www/.test('Tom'))             console.log('Hello Tom');
    if (/[a-zA-Z]/.test('B'))             console.log('Letter');
    

    Character matching[edit]

    // A...Z a...z 0...9  - alphanumeric
    // u0000...uFFFF    - Unicode hexadecimal
    // x00...xFF        - ASCII hexadecimal
    // t                 - tab
    // n                 - new line
    // r                 - CR
    // .                  - any character
    // |                  - OR
    
    if (/T.m/.test('Tom')) console.log ('Hi Tom, Tam or Tim');
    if (/A|B/.test("A"))  console.log ('A or B');
    

    Repeaters[edit]

    // ?   - 0 or 1 match
    // *   - 0 or more
    // +   - 1 or more
    // {n}   - exactly n
    // {n,}  - n or more
    // {0,n} - n or less
    // {n,m} - range n to m
    
    if (/ab?c/.test("ac"))       console.log("OK"); // match: "ac", "abc"
    if (/ab*c/.test("ac"))       console.log("OK"); // match: "ac", "abc", "abbc", "abbbc" etc.
    if (/ab+c/.test("abc"))      console.log("OK"); // match: "abc", "abbc", "abbbc" etc.
    if (/ab{3}c/.test("abbbc"))  console.log("OK"); // match: "abbbc"
    if (/ab{3,}c/.test("abbbc")) console.log("OK"); // match: "abbbc", "abbbbc", "abbbbbc" etc.
    if (/ab{1,3}c/.test("abc"))  console.log("OK"); // match: "abc", "abbc", "abbbc"
    

    Anchors[edit]

    // ^  - string starts with
    // $  - string ends with
    
    if (/^My/.test("My name is Tom"))   console.log ("Hi!");
    if (/Tom$/.test("My name is Tom"))  console.log ("Hi Tom!");
    

    Subexpression[edit]

    // ( )  - groups characters
    
    if (/water(mark)?/.test("watermark")) console.log("Here is water!"); // match: "water", "watermark",
    if (/(Tom)|(John)/.test("John"))      console.log("Hi Tom or John!");
    

    Flags[edit]

    // /g  - global
    // /i  - ignore upper/lower case
    // /m  - allow matches to span multiple lines
    
    console.log("hi tom!".replace(/Tom/i, "John"));  // == "hi John!"
    console.log("ratatam".replace(/ta/, "tu"));      // == "ratutam"
    console.log("ratatam".replace(/ta/g, "tu"));     // == "ratutum"
    

    Advanced methods[edit]

    my_array = my_string.split(my_delimiter);
    // example
    my_array = "dog,cat,cow".split(",");      // my_array==["dog","cat","cow"];
    
    my_array = my_string.match(my_expression);
    // example
    my_array = "We start at 11:30, 12:15 and 16:45".match(/dd:dd/g); // my_array==["11:30","12:15","16:45"];
    

    Capturing groups[edit]

    var myRe = /(d{4}-d{2}-d{2}) (d{2}:d{2}:d{2})/;
    var results = myRe.exec("The date and time are 2009-09-08 09:37:08.");
    if (results) {
      console.log("Matched: " + results[0]); // Entire match
      var my_date = results[1]; // First group == "2009-09-08"
      var my_time = results[2]; // Second group == "09:37:08"
      console.log("It is " + my_time + " on " + my_date);
    } else console.log("Did not find a valid date!");
    

    Function[edit]

    Every function in JavaScript is an instance of the Function constructor:

    // x, y is the argument. 'return x + y' is the function body, which is the last in the argument list.
    var add = new Function('x', 'y', 'return x + y');
    add(1, 2); // => 3
    

    The add function above may also be defined using a function expression:

    var add = function(x, y) {
      return x + y;
    };
    add(1, 2); // => 3
    

    In ES6, arrow function syntax was added, allowing functions that return a value to be more concise. They also retain the this of the global object instead of inheriting it from where it was called / what it was called on, unlike the function() {} expression.

    var add = (x, y) => {return x + y;};
    // values can also be implicitly returned (i.e. no return statement is needed)
    var addImplicit = (x, y) => x + y;
    
    add(1, 2); // => 3
    addImplicit(1, 2) // => 3
    

    For functions that need to be hoisted, there is a separate expression:

    function add(x, y) {
      return x + y;
    }
    add(1, 2); // => 3
    

    Hoisting allows you to use the function before it is «declared»:

    add(1, 2); // => 3, not a ReferenceError
    function add(x, y) {
      return x + y;
    }
    

    A function instance has properties and methods.

    function subtract(x, y) {
      return x - y;
    }
    
    console.log(subtract.length); // => 2, arity of the function (number of arguments)
    console.log(subtract.toString());
    
    /*
    "function subtract(x, y) {
      return x - y;
    }"
    */
    

    Operators[edit]

    The ‘+’ operator is overloaded: it is used for string concatenation and arithmetic addition. This may cause problems when inadvertently mixing strings and numbers. As a unary operator, it can convert a numeric string to a number.

    // Concatenate 2 strings
    console.log('He' + 'llo'); // displays Hello
    
    // Add two numbers
    console.log(2 + 6);  // displays 8
    
    // Adding a number and a string results in concatenation (from left to right)
    console.log(2 + '2');    // displays 22
    console.log('$' + 3 + 4);  // displays $34, but $7 may have been expected
    console.log('$' + (3 + 4)); // displays $7
    console.log(3 + 4 + '7'); // displays 77, numbers stay numbers until a string is added
    
    // Convert a string to a number using the unary plus
    console.log(+'2' === 2); // displays true
    console.log(+'Hello'); // displays NaN
    

    Similarly, the ‘*’ operator is overloaded: it can convert a string into a number.

    console.log(2 + '6'*1);  // displays 8
    console.log(3*'7'); // 21
    console.log('3'*'7'); // 21
    console.log('hello'*'world'); // displays NaN
    

    Arithmetic[edit]

    JavaScript supports the following binary arithmetic operators:

    + addition
    - subtraction
    * multiplication
    / division (returns a floating-point value)
    % modulo (returns the remainder)
    ** exponentiation

    JavaScript supports the following unary arithmetic operators:

    + unary conversion of string to number
    - unary negation (reverses the sign)
    ++ increment (can be prefix or postfix)
    -- decrement (can be prefix or postfix)
    var x = 1;
    console.log(++x); // x becomes 2; displays 2
    console.log(x++); // displays 2; x becomes 3
    console.log(x);  // x is 3; displays 3
    console.log(x--); //  displays 3; x becomes 2
    console.log(x);  //  displays 2; x is 2
    console.log(--x); //  x becomes 1; displays 1
    

    The modulo operator displays the remainder after division by the modulus. If negative numbers are involved, the returned value depends on the operand.

    var x = 17;
    console.log(x%5); // displays 2
    console.log(x%6); // displays 5
    console.log(-x%5); // displays -2
    console.log(-x%-5); // displays -2
    console.log(x%-5); // displays 2
    

    To always return a non-negative number, re-add the modulus and apply the modulo operator again:

    var x = 17;
    console.log((-x%5+5)%5); // displays 3
    

    You could also do:

    var x = 17;
    console.log(Math.abs(-x%5)); // also 3
    

    Assignment[edit]

    = assign
    += add and assign
    -= subtract and assign
    *= multiply and assign
    /= divide and assign
    %= modulo and assign
    **= exponentiation and assign

    Assignment of primitive types

    var x = 9;
    x += 1; 
    console.log(x); // displays: 10
    x *= 30;
    console.log(x); // displays: 300
    x /= 6;
    console.log(x); // displays: 50
    x -= 3;
    console.log(x); // displays: 47
    x %= 7;
    console.log(x); // displays: 5
    

    Assignment of object types

    /**
     * To learn JavaScript objects...
     */
    var object_1 = {a: 1};		// assign reference of newly created object to object_1
    var object_2 = {a: 0};
    var object_3 = object_2;	// object_3 references the same object as object_2 does
    	 
    object_3.a = 2;
    message();	        	// displays 1 2 2
    	 
    object_2 = object_1;		// object_2 now references the same object as object_1
    	        	        // object_3 still references what object_2 referenced before
    message();		        // displays 1 1 2
    	 
    object_2.a = 7;  	        // modifies object_1
    message();		        // displays 7 7 2
    
    object_3.a = 5;                 // object_3 doesn't change object_2
    message();	                // displays 7 7 5
    
    object_3 = object_2;	
    object_3.a=4;                  // object_3 changes object_1 and object_2
    message();                     // displays 4 4 4
    
    /**
     * Prints the console.log message
     */
    function message() {
    	console.log(object_1.a + " " + object_2.a + " " + object_3.a);
    }
    

    Destructuring assignment[edit]

    In Mozilla’s JavaScript, since version 1.7, destructuring assignment allows the assignment of parts of data structures to several variables at once. The left hand side of an assignment is a pattern that resembles an arbitrarily nested object/array literal containing l-lvalues at its leaves that are to receive the substructures of the assigned value.

    var a, b, c, d, e;
    [a, b, c] = [3, 4, 5];
    console.log(a + ',' + b + ',' + c); // displays: 3,4,5
    e = {foo: 5, bar: 6, baz: ['Baz', 'Content']};
    var arr = [];
    ({baz: [arr[0], arr[3]], foo: a, bar: b}) = e;
    console.log(a + ',' + b + ',' + arr);	// displays: 5,6,Baz,,,Content
    [a, b] = [b, a];		// swap contents of a and b
    console.log(a + ',' + b);		// displays: 6,5
    
    [a, b, c] = [3, 4, 5]; // permutations
    [a, b, c] = [b, c, a];
    console.log(a + ',' + b + ',' + c); // displays: 4,5,3
    

    Spread/rest operator[edit]

    The ECMAScript 2015 standard introduces the «...» operator, for the related concepts of «spread syntax»[11] and «rest parameters»[12]

    Spread syntax provides another way to destructure arrays. It indicates that the elements in a specified array should be used as the parameters in a function call or the items in an array literal.

    In other words, «...» transforms «[...foo]» into «[foo[0], foo[1], foo[2]]«, and «this.bar(...foo);» into «this.bar(foo[0], foo[1], foo[2]);«.

    var a = [1, 2, 3, 4];
    
    // It can be used multiple times in the same expression
    var b = [...a, ...a]; // b = [1, 2, 3, 4, 1, 2, 3, 4];
    
    // It can be combined with non-spread items.
    var c = [5, 6, ...a, 7, 9]; // c = [5, 6, 1, 2, 3, 4, 7, 9];
    
    // For comparison, doing this without the spread operator 
    // creates a nested array.
    var d = [a, a]; // d = [[1, 2, 3, 4], [1, 2, 3, 4]]
    
    // It works the same with function calls
    function foo(arg1, arg2, arg3) {
        console.log(arg1 + ':' + arg2 + ':' + arg3);
    }
    
    // You can use it even if it passes more parameters than the function will use
    foo(...a); // "1:2:3" → foo(a[0], a[1], a[2], a[3]);
    
    // You can mix it with non-spread parameters
    foo(5, ...a, 6); // "5:1:2" → foo(5, a[0], a[1], a[2], a[3], 6);
    
    // For comparison, doing this without the spread operator
    // assigns the array to arg1, and nothing to the other parameters.
    foo(a); // "1,2,3,4:undefined:undefined"
    

    When ... is used in a function declaration, it indicates a rest parameter. The rest parameter must be the final named parameter in the function’s parameter list. It will be assigned an Array containing any arguments passed to the function in excess of the other named parameters. In other words, it gets «the rest» of the arguments passed to the function (hence the name).

    function foo(a, b, ...c) {
        console.log(c.length);
    }
    
    foo(1, 2, 3, 4, 5); // "3" → c = [3, 4, 5]
    foo('a', 'b'); // "0" → c = []
    

    Rest parameters are similar to Javascript’s arguments object, which is an array-like object that contains all of the parameters (named and unnamed) in the current function call. Unlike arguments, however, rest parameters are true Array objects, so methods such as .slice() and .sort() can be used on them directly.

    The ... operator can only be used with Array objects. (However, there is a proposal to extend it to Objects in a future ECMAScript standard.[13])

    Comparison[edit]

    == equal
    != not equal
    > greater than
    >= greater than or equal to
    < less than
    <= less than or equal to
    === identical (equal and of same type)
    !== not identical

    Variables referencing objects are equal or identical only if they reference the same object:

    var obj1 = {a: 1};
    var obj2 = {a: 1};
    var obj3 = obj1;
    console.log(obj1 == obj2);  //false
    console.log(obj3 == obj1);  //true
    console.log(obj3 === obj1); //true
    

    See also String.

    Logical[edit]

    JavaScript provides four logical operators:

    • unary negation (NOT = !a)
    • binary disjunction (OR = a || b) and conjunction (AND = a && b)
    • ternary conditional (c ? t : f)

    In the context of a logical operation, any expression evaluates to true except the following:

    • Strings: "", '',
    • Numbers: 0, -0, NaN,
    • Special: null, undefined,
    • Boolean: false.

    The Boolean function can be used to explicitly convert to a primitive of type Boolean:

    // Only empty strings return false
    console.log(Boolean("")      === false);
    console.log(Boolean("false") === true);
    console.log(Boolean("0")     === true);
    
    // Only zero and NaN return false
    console.log(Boolean(NaN) === false);
    console.log(Boolean(0)   === false);
    console.log(Boolean(-0)  === false); // equivalent to -1*0
    console.log(Boolean(-2)  === true);
    
    // All objects return true
    console.log(Boolean(this) === true);
    console.log(Boolean({})   === true);
    console.log(Boolean([])   === true);
    
    // These types return false
    console.log(Boolean(null)      === false);
    console.log(Boolean(undefined) === false); // equivalent to Boolean()
    

    The NOT operator evaluates its operand as a Boolean and returns the negation. Using the operator twice in a row, as a double negative, explicitly converts an expression to a primitive of type Boolean:

    console.log( !0 === Boolean(!0));
    console.log(Boolean(!0) === !!1);
    console.log(!!1 === Boolean(1));
    console.log(!!0 === Boolean(0));
    console.log(Boolean(0) === !1);
    console.log(!1 === Boolean(!1));
    console.log(!"" === Boolean(!""));
    console.log(Boolean(!"") === !!"s");
    console.log(!!"s" === Boolean("s"));
    console.log(!!"" === Boolean(""));
    console.log(Boolean("") === !"s");	
    console.log(!"s" === Boolean(!"s"));
    

    The ternary operator can also be used for explicit conversion:

    console.log([] == false); console.log([] ? true : false); // “truthy”, but the comparison uses [].toString()
    console.log([0] == false); console.log([0]? true : false); // [0].toString() == "0"
    console.log("0" == false); console.log("0"? true : false); // "0" → 0 ... (0 == 0) ... 0 ← false
    console.log([1] == true); console.log([1]? true : false); // [1].toString() == "1"
    console.log("1" == true); console.log("1"? true : false); // "1" → 1 ... (1 == 1) ... 1 ← true
    console.log([2] != true); console.log([2]? true : false); // [2].toString() == "2"
    console.log("2" != true); console.log("2"? true : false); // "2" → 2 ... (2 != 1) ... 1 ← true
    

    Expressions that use features such as post–incrementation (i++) have an anticipated side effect. JavaScript provides short-circuit evaluation of expressions; the right operand is only executed if the left operand does not suffice to determine the value of the expression.

    console.log(a || b);  // When a is true, there is no reason to evaluate b.
    console.log(a && b);  // When a is false, there is no reason to evaluate b.
    console.log(c ? t : f); // When c is true, there is no reason to evaluate f.
    

    In early versions of JavaScript and JScript, the binary logical operators returned a Boolean value (like most C-derived programming languages). However, all contemporary implementations return one of their operands instead:

    console.log(a || b); // if a is true, return a, otherwise return b
    console.log(a && b); // if a is false, return a, otherwise return b
    

    Programmers who are more familiar with the behavior in C might find this feature surprising, but it allows for a more concise expression of patterns like null coalescing:

    var s = t || "(default)"; // assigns t, or the default value, if t is null, empty, etc.
    

    Logical assignment[edit]

    ??= Nullish assignment
    ||= Logical Or assignment
    &&= Logical And assignment

    Bitwise[edit]

    [icon]

    This section needs expansion. You can help by adding to it. (April 2011)

    JavaScript supports the following binary bitwise operators:

    & AND
    | OR
    ^ XOR
    ! NOT
    << shift left (zero fill at right)
    >> shift right (sign-propagating); copies of the
    leftmost bit (sign bit) are shifted in from the left
    >>> shift right (zero fill at left). For positive numbers,
    >> and >>> yield the same result.

    Examples:

    x=11 & 6;
    console.log(x); // 2
    

    JavaScript supports the following unary bitwise operator:

    Bitwise Assignment[edit]

    JavaScript supports the following binary assignment operators:

    &= and
    |= or
    ^= xor
    <<= shift left (zero fill at right)
    >>= shift right (sign-propagating); copies of the
    leftmost bit (sign bit) are shifted in from the left
    >>>= shift right (zero fill at left). For positive numbers,
    >>= and >>>= yield the same result.

    Examples:

    x=7;
    console.log(x); // 7
    x<<=3;
    console.log(x); // 7->14->28->56
    

    String[edit]

    = assignment
    + concatenation
    += concatenate and assign

    Examples:

    str = "ab" + "cd";  // "abcd"
    str += "e";      // "abcde"
    
    str2 = "2" + 2;     // "22", not "4" or 4.
    

    ??[edit]

    JavaScript’s nearest operator is ??, the «nullish coalescing operator,» which was added to the standard in ECMAScript’s 11th edition.[14] In earlier versions, it could be used via a Babel plugin, and in TypeScript. It evaluates its left-hand operand and, if the result value is not «nullish» (null or undefined), takes that value as its result; otherwise, it evaluates the right-hand operand and takes the resulting value as its result.

    In the following example, a will be assigned the value of b if the value of b is not null or undefined, otherwise it will be assigned 3.

    Before the nullish coalescing operator, programmers would use the logical OR operator (||). But where ?? looks specifically for null or undefined, the || operator looks for any falsy value: null, undefined, "", 0, NaN, and of course, false.

    In the following example, a will be assigned the value of b if the value of b is truthy, otherwise it will be assigned 3.

    Control structures[edit]

    Compound statements[edit]

    A pair of curly brackets { } and an enclosed sequence of statements constitute a compound statement, which can be used wherever a statement can be used.

    If … else[edit]

    if (expr) {
      //statements;
    } else if (expr2) {
      //statements;
    } else {
      //statements;
    }
    

    Conditional (ternary) operator[edit]

    The conditional operator creates an expression that evaluates as one of two expressions depending on a condition. This is similar to the if statement that selects one of two statements to execute depending on a condition. I.e., the conditional operator is to expressions what if is to statements.

     result = condition ? expression : alternative;
    

    is the same as:

     if (condition) {
      result = expression;
     } else {
      result = alternative;
     }
    

    Unlike the if statement, the conditional operator cannot omit its «else-branch».

    Switch statement[edit]

    The syntax of the JavaScript switch statement is as follows:

     switch (expr) {
      case SOMEVALUE:
       // statements;
       break;
      case ANOTHERVALUE:
        // statements for when ANOTHERVALUE || ORNAOTHERONE
        // no break statement, falling through to the following case
      case ORANOTHERONE:
       // statements specific to ORANOTHERONE (i.e. !ANOTHERVALUE && ORANOTHER);
       break; //The buck stops here.
      case YETANOTHER:
       // statements;
       break;
      default:
       // statements;
       break;
     }
    
    • break; is optional; however, it is usually needed, since otherwise code execution will continue to the body of the next case block. This fall through behavior can be used when the same set of statements apply in several cases, effectively creating a disjunction between those cases.
    • Add a break statement to the end of the last case as a precautionary measure, in case additional cases are added later.
    • String literal values can also be used for the case values.
    • Expressions can be used instead of values.
    • The default case (optional) is executed when the expression does not match any other specified cases.
    • Braces are required.

    For loop[edit]

    The syntax of the JavaScript for loop is as follows:

     for (initial; condition; loop statement) {
      /*
       statements will be executed every time
       the for{} loop cycles, while the
       condition is satisfied
      */
     }
    

    or

     for (initial; condition; loop statement(iteration)) // one statement
    

    For … in loop[edit]

    The syntax of the JavaScript for ... in loop is as follows:

    for (var property_name in some_object) {
      // statements using some_object[property_name];
    }
    
    • Iterates through all enumerable properties of an object.
    • Iterates through all used indices of array including all user-defined properties of array object, if any. Thus it may be better to use a traditional for loop with a numeric index when iterating over arrays.
    • There are differences between the various Web browsers with regard to which properties will be reflected with the for…in loop statement. In theory, this is controlled by an internal state property defined by the ECMAscript standard called «DontEnum», but in practice, each browser returns a slightly different set of properties during introspection. It is useful to test for a given property using if (some_object.hasOwnProperty(property_name)) { ...}. Thus, adding a method to the array prototype with Array.prototype.newMethod = function() {...} may cause for ... in loops to loop over the method’s name.

    While loop[edit]

    The syntax of the JavaScript while loop is as follows:

    while (condition) {
      statement1;
      statement2;
      statement3;
      ...
    }
    

    Do … while loop[edit]

    The syntax of the JavaScript do ... while loop is as follows:

    do {
      statement1;
      statement2;
      statement3;
      ...
    } while (condition);
    

    With[edit]

    The with statement adds all of the given object’s properties and methods into the following block’s scope, letting them be referenced as if they were local variables.

    with (document) {
      var a = getElementById('a');
      var b = getElementById('b');
      var c = getElementById('c');
    };
    
    • Note the absence of document. before each getElementById() invocation.

    The semantics are similar to the with statement of Pascal.

    Because the availability of with statements hinders program performance and is believed to reduce code clarity (since any given variable could actually be a property from an enclosing with), this statement is not allowed in strict mode.

    Labels[edit]

    JavaScript supports nested labels in most implementations. Loops or blocks can be labelled for the break statement, and loops for continue. Although goto is a reserved word,[15] goto is not implemented in JavaScript.

    loop1: for (var a = 0; a < 10; ++a) {
      if (a === 4) break loop1; // Stops after the 4th attempt
      console.log('a = ' + a);
      loop2: for (var b = 0; b < 10; ++b) {
        if (b === 3) continue loop2; // Number 3 is skipped
        if (b === 6) continue loop1; // Continues the first loop, 'finished' is not shown
        console.log('b = ' + b);
      } //end of loop2
      console.log('finished');
    } //end of loop1
    block1: {
      console.log('Hello'); // Displays 'Hello'
      break block1;
      console.log('World'); // Will never get here
    }
    goto block1; // Parse error.
    

    Functions[edit]

    A function is a block with a (possibly empty) parameter list that is normally given a name. A function may use local variables. If you exit the function without a return statement, the value undefined is returned.

    function gcd(number1, number2) {
      if isNaN(number1*number2) throw TypeError("Non-Numeric arguments not allowed.");
      number1 = Math.round(number1);
      number2 = Math.round(number2);
      let difference = number1 - number2;
      if (difference === 0) return number1;
      return difference > 0 ? gcd(number2, difference) : gcd(number1, -difference);
    }
    console.log(gcd(60, 40)); // 20
    
    //In the absence of parentheses following the identifier 'gcd' on the RHS of the assignment below,
    //'gcd' returns a reference to the function itself without invoking it.
    let mygcd = gcd; // mygcd and gcd reference the same function.
    console.log(mygcd(60, 40)); // 20
    

    Functions are first class objects and may be assigned to other variables.

    The number of arguments given when calling a function may not necessarily correspond to the number of arguments in the function definition; a named argument in the definition that does not have a matching argument in the call will have the value undefined (that can be implicitly cast to false). Within the function, the arguments may also be accessed through the arguments object; this provides access to all arguments using indices (e.g. arguments[0], arguments[1], ... arguments[n]), including those beyond the number of named arguments. (While the arguments list has a .length property, it is not an instance of Array; it does not have methods such as .slice(), .sort(), etc.)

    function add7(x, y) {
      if (!y) {
        y = 7;
      }
      console.log(x + y + arguments.length);
    };
    add7(3); // 11
    add7(3, 4); // 9
    

    Primitive values (number, boolean, string) are passed by value. For objects, it is the reference to the object that is passed.

    var obj1 = {a : 1};
    var obj2 = {b : 2};
    function foo(p) {
      p = obj2; // Ignores actual parameter
      p.b = arguments[1];
    }
    foo(obj1, 3); // Does not affect obj1 at all. 3 is additional parameter
    console.log(obj1.a + " " + obj2.b); // writes 1 3
    

    Functions can be declared inside other functions, and access the outer function’s local variables. Furthermore, they implement full closures by remembering the outer function’s local variables even after the outer function has exited.

    let t = "Top";
    let bar, baz;
    function foo() {
      let f = "foo var";
      bar = function() { console.log(f) };
      baz = function(x) { f = x; };
    }
    foo();
    baz("baz arg");
    bar(); // "baz arg" (not "foo var") even though foo() has exited.
    console.log(t); // Top
    

    Async/await[edit]

    The await operator in JavaScript can only be used from inside an async function. If the parameter is a promise, execution of the async function will resume when the promise is resolved (unless the promise is rejected, in which case an error will be thrown that can be handled with normal JavaScript exception handling). If the parameter is not a promise, the parameter itself will be returned immediately.[16]

    Many libraries provide promise objects that can also be used with await, as long as they match the specification for native JavaScript promises. However, promises from the jQuery library were not Promises/A+ compatible until jQuery 3.0.[17]

    Here’s an example (modified from this[18] article):

    async function createNewDoc() {
      let response = await db.post({}); // post a new doc
      return db.get(response.id); // find by id
    }
    
    async function main() {
      try {
        let doc = await createNewDoc();
        console.log(doc);
      } catch (err) {
        console.log(err);
      }
    }
    main();
    

    Node.js version 8 includes a utility that enables using the standard library callback-based methods as promises.[19]

    Objects[edit]

    For convenience, types are normally subdivided into primitives and objects. Objects are entities that have an identity (they are only equal to themselves) and that map property names to values («slots» in prototype-based programming terminology). Objects may be thought of as associative arrays or hashes, and are often implemented using these data structures. However, objects have additional features, such as a prototype chain[clarification needed], which ordinary associative arrays do not have.

    JavaScript has several kinds of built-in objects, namely Array, Boolean, Date, Function, Math, Number, Object, RegExp and String. Other objects are «host objects», defined not by the language, but by the runtime environment. For example, in a browser, typical host objects belong to the DOM (window, form, links, etc.).

    Creating objects[edit]

    Objects can be created using a constructor or an object literal. The constructor can use either a built-in Object function or a custom function. It is a convention that constructor functions are given a name that starts with a capital letter:

    // Constructor
    var anObject = new Object();
    
    // Object literal
    var objectA = {};
    var objectA2 = {};  // A != A2, {}s create new objects as copies.
    var objectB = {index1: 'value 1', index2: 'value 2'};
    
    // Custom constructor (see below)
    

    Object literals and array literals allow one to easily create flexible data structures:

    var myStructure = {
      name: {
        first: "Mel",
        last: "Smith"
      },
      age: 33,
      hobbies: ["chess", "jogging"]
    };
    

    This is the basis for JSON, which is a simple notation that uses JavaScript-like syntax for data exchange.

    Methods[edit]

    A method is simply a function that has been assigned to a property name of an object. Unlike many object-oriented languages, there is no distinction between a function definition and a method definition in object-related JavaScript. Rather, the distinction occurs during function calling; a function can be called as a method.

    When called as a method, the standard local variable this is just automatically set to the object instance to the left of the «.«. (There are also call and apply methods that can set this explicitly—some packages such as jQuery do unusual things with this.)

    In the example below, Foo is being used as a constructor. There is nothing special about a constructor — it is just a plain function that initialises an object. When used with the new keyword, as is the norm, this is set to a newly created blank object.

    Note that in the example below, Foo is simply assigning values to slots, some of which are functions. Thus it can assign different functions to different instances. There is no prototyping in this example.

    function px() { return this.prefix + "X"; }
    
    function Foo(yz) {
      this.prefix = "a-";
      if (yz > 0) {
        this.pyz = function() { return this.prefix + "Y"; };
      } else {
        this.pyz = function() { return this.prefix + "Z"; };
      }
      this.m1 = px;
      return this;
    }
    
    var foo1 = new Foo(1);
    var foo2 = new Foo(0);
    foo2.prefix = "b-";
    
    console.log("foo1/2 " + foo1.pyz() + foo2.pyz());
    // foo1/2 a-Y b-Z
    
    foo1.m3 = px; // Assigns the function itself, not its evaluated result, i.e. not px()
    var baz = {"prefix": "c-"};
    baz.m4 = px; // No need for a constructor to make an object.
    
    console.log("m1/m3/m4 " + foo1.m1() + foo1.m3() + baz.m4());
    // m1/m3/m4 a-X a-X c-X
    
    foo1.m2(); // Throws an exception, because foo1.m2 doesn't exist.
    

    Constructors[edit]

    Constructor functions simply assign values to slots of a newly created object. The values may be data or other functions.

    Example: Manipulating an object:

    function MyObject(attributeA, attributeB) {
      this.attributeA = attributeA;
      this.attributeB = attributeB;
    }
    
    MyObject.staticC = "blue"; // On MyObject Function, not object
    console.log(MyObject.staticC); // blue
    
    object = new MyObject('red', 1000);
    
    console.log(object.attributeA); // red
    console.log(object["attributeB"]); // 1000
    
    console.log(object.staticC); // undefined
    object.attributeC = new Date(); // add a new property
    
    delete object.attributeB; // remove a property of object
    console.log(object.attributeB); // undefined
    

    The constructor itself is referenced in the object’s prototype’s constructor slot. So,

    function Foo() {}
    // Use of 'new' sets prototype slots (for example, 
    // x = new Foo() would set x's prototype to Foo.prototype,
    // and Foo.prototype has a constructor slot pointing back to Foo).
    x = new Foo();
    // The above is almost equivalent to
    y = {};
    y.constructor = Foo;
    y.constructor();
    // Except
    x.constructor == y.constructor // true
    x instanceof Foo // true
    y instanceof Foo // false
    // y's prototype is Object.prototype, not
    // Foo.prototype, since it was initialised with
    // {} instead of new Foo.
    // Even though Foo is set to y's constructor slot,
    // this is ignored by instanceof - only y's prototype's
    // constructor slot is considered.
    

    Functions are objects themselves, which can be used to produce an effect similar to «static properties» (using C++/Java terminology) as shown below. (The function object also has a special prototype property, as discussed in the «Inheritance» section below.)

    Object deletion is rarely used as the scripting engine will garbage collect objects that are no longer being referenced.

    Inheritance[edit]

    JavaScript supports inheritance hierarchies through prototyping in the manner of Self.

    In the following example, the Derived class inherits from the Base class.
    When d is created as Derived, the reference to the base instance of Base is copied to d.base.

    Derive does not contain a value for aBaseFunction, so it is retrieved from aBaseFunction when aBaseFunction is accessed. This is made clear by changing the value of base.aBaseFunction, which is reflected in the value of d.aBaseFunction.

    Some implementations allow the prototype to be accessed or set explicitly using the __proto__ slot as shown below.

    function Base() {
      this.anOverride = function() { console.log("Base::anOverride()"); };
    
      this.aBaseFunction = function() { console.log("Base::aBaseFunction()"); };
    }
    
    function Derived() {
      this.anOverride = function() { console.log("Derived::anOverride()"); };
    }
    
    base = new Base();
    Derived.prototype = base; // Must be before new Derived()
    Derived.prototype.constructor = Derived; // Required to make `instanceof` work
    
    d = new Derived();    // Copies Derived.prototype to d instance's hidden prototype slot.
    d instanceof Derived; // true
    d instanceof Base;    // true
    
    base.aBaseFunction = function() { console.log("Base::aNEWBaseFunction()"); }
    
    d.anOverride();    // Derived::anOverride()
    d.aBaseFunction(); // Base::aNEWBaseFunction()
    console.log(d.aBaseFunction == Derived.prototype.aBaseFunction); // true
    
    console.log(d.__proto__ == base); // true in Mozilla-based implementations and false in many others.
    

    The following shows clearly how references to prototypes are copied on instance creation, but that changes to a prototype can affect all instances that refer to it.

    function m1() { return "One"; }
    function m2() { return "Two"; }
    function m3() { return "Three"; }
    
    function Base() {}
    
    Base.prototype.m = m2;
    bar = new Base();
    console.log("bar.m " + bar.m()); // bar.m Two
    
    function Top() { this.m = m3; }
    t = new Top();
    
    foo = new Base();
    Base.prototype = t;
    // No effect on foo, the *reference* to t is copied.
    console.log("foo.m " + foo.m()); // foo.m Two
    
    baz = new Base();
    console.log("baz.m " + baz.m()); // baz.m Three
    
    t.m = m1; // Does affect baz, and any other derived classes.
    console.log("baz.m1 " + baz.m()); // baz.m1 One
    

    In practice many variations of these themes are used, and it can be both powerful and confusing.

    Exception handling[edit]

    JavaScript includes a try ... catch ... finally exception handling statement to handle run-time errors.

    The try ... catch ... finally statement catches exceptions resulting from an error or a throw statement. Its syntax is as follows:

    try {
      // Statements in which exceptions might be thrown
    } catch(errorValue) {
      // Statements that execute in the event of an exception
    } finally {
      // Statements that execute afterward either way
    }
    

    Initially, the statements within the try block execute. If an exception is thrown, the script’s control flow immediately transfers to the statements in the catch block, with the exception available as the error argument. Otherwise the catch block is skipped. The catch block can throw(errorValue), if it does not want to handle a specific error.

    In any case the statements in the finally block are always executed. This can be used to free resources, although memory is automatically garbage collected.

    Either the catch or the finally clause may be omitted. The catch argument is required.

    The Mozilla implementation allows for multiple catch statements, as an extension to the ECMAScript standard. They follow a syntax similar to that used in Java:

    try { statement; }
    catch (e if e == "InvalidNameException")  { statement; }
    catch (e if e == "InvalidIdException")    { statement; }
    catch (e if e == "InvalidEmailException") { statement; }
    catch (e)                                 { statement; }
    

    In a browser, the onerror event is more commonly used to trap exceptions.

    onerror = function (errorValue, url, lineNr) {...; return true;};
    

    Native functions and methods[edit]

    (Not related to Web browsers.)

    eval (expression)[edit]

    Evaluates the first parameter as an expression, which can include assignment statements. Variables local to functions can be referenced by the expression. However, eval represents a major security risk, as it allows a bad actor to execute arbitrary code, so its use is discouraged.[20]

    > (function foo() {
    ...   var x = 7;
    ...   console.log("val " + eval("x + 2"));
    ... })();
    val 9
    undefined
    

    See also[edit]

    • Comparison of JavaScript-based source code editors
    • JavaScript

    References[edit]

    1. ^ JavaScript 1.1 specification
    2. ^ «Chapter 1. Basic JavaScript». speakingjs.com. Retrieved 22 September 2020.
    3. ^ Flanagan, David (2006). JavaScript: The definitive Guide. p. 16. ISBN 978-0-596-10199-2. Omitting semicolons is not a good programming practice; you should get into the habit of inserting them.
    4. ^ «Values, Variables, and Literals — MDC». Mozilla Developer Network. 16 September 2010. Archived from the original on 29 June 2011. Retrieved 1 February 2020.
    5. ^ «JavaScript Hoisting». W3Schools. In JavaScript, a variable can be declared after it has been used. In other words; a variable can be used before it has been declared.
    6. ^ «JavaScript Scoping and Hoisting», Ben Cherry, Adequately Good, 2010-02-08
    7. ^ ECMA-262 5e edition clarified this behavior with the Declarative Environment Record and Object Environment Record. With this formalism, the global object is the Object Environment Record of the global Lexical Environment (the global scope).
    8. ^ «Template literals». MDN Web Docs. Retrieved 2 May 2018.
    9. ^ «Comparison Operators — MDC Doc Center». Mozilla. 5 August 2010. Retrieved 5 March 2011.
    10. ^ «The Elements of JavaScript Style». Douglas Crockford. Retrieved 5 March 2011.
    11. ^ «Spread syntax».
    12. ^ «rest parameters».
    13. ^ «Ecmascript». Archived from the original on 9 August 2016.
    14. ^ «ECMAScript 2020 Language Specification». Ecma International. June 2020.
    15. ^ ECMA-262, Edition 3, 7.5.3 Future Reserved Words
    16. ^ «await — JavaScript (MDN)». Retrieved 2 May 2017.
    17. ^ «jQuery Core 3.0 Upgrade Guide». Retrieved 2 May 2017.
    18. ^ «Taming the asynchronous beast with ES7». Retrieved 12 November 2015.
    19. ^ Foundation, Node.js. «Node v8.0.0 (Current) — Node.js». Node.js.
    20. ^ «eval()». MDN Web Docs. Retrieved 29 January 2020.

    Further reading[edit]

    • Danny Goodman: JavaScript Bible, Wiley, John & Sons, ISBN 0-7645-3342-8.
    • David Flanagan, Paula Ferguson: JavaScript: The Definitive Guide, O’Reilly & Associates, ISBN 0-596-10199-6.
    • Thomas A. Powell, Fritz Schneider: JavaScript: The Complete Reference, McGraw-Hill Companies, ISBN 0-07-219127-9.
    • Axel Rauschmayer: Speaking JavaScript: An In-Depth Guide for Programmers, 460 pages, O’Reilly Media, 25 February 2014, ISBN 978-1449365035. (free online edition)
    • Emily Vander Veer: JavaScript For Dummies, 4th Edition, Wiley, ISBN 0-7645-7659-3.

    External links[edit]

    • A re-introduction to JavaScript — Mozilla Developer Center
    • JavaScript Loops
    • ECMAScript standard references: ECMA-262
    • Interactive JavaScript Lessons — example-based
    • JavaScript on About.com: lessons and explanation Archived 25 February 2017 at the Wayback Machine
    • JavaScript Training
    • Mozilla Developer Center Core References for JavaScript versions 1.5, 1.4, 1.3 and 1.2
    • Mozilla JavaScript Language Documentation

    Понравилась статья? Поделить с друзьями:
  • Как правильно пишется its
  • Как правильно пишется iphone
  • Как правильно пишется internet
  • Как правильно пишется intelligent
  • Как правильно пишется icloud логин