← Блог · · Команда ConvertCaseHQ · 7 мин чтения

Именование переменных в JavaScript: руководство по camelCase

В JavaScript переменные, функции, параметры и свойства объектов называйте в camelCase (cartTotal, fetchOrders), классы и компоненты React — в PascalCase (ShoppingCart), а CONSTANT_CASE оставьте для значений, которые не меняются всё время работы программы (MAX_RETRIES). Руководства по стилю JavaScript от Google и Airbnb сходятся по меньшей мере в этом, и встроенные объекты самого JavaScript следуют тому же образцу.

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

Что разрешает сам JavaScript

Правила языка коротки. Идентификатор начинается с буквы, подчёркивания или знака доллара, а следующими символами могут быть ещё и цифры. Буквы не ограничиваются ASCII: в собственном примере MDN используется немецкое слово Früh. Имена чувствительны к регистру, поэтому total и Total — две разные переменные, а зарезервированные слова вроде new, class и delete нельзя использовать как имена переменных.

let total = 1;
let Total = 2;      // a different variable
const café = "ok";  // Unicode letters are allowed
const $price = 9.99;
const _unused = null;

// const 2fast = true;  // SyntaxError: can't start with a digit
// const new = 1;       // SyntaxError: reserved word

Имена свойств ещё свободнее: подойдёт любая строка, если взять её в кавычки, поэтому возможно headers["Content-Type"]. Всё, что выходит за рамки этих правил, — соглашения, и именно поэтому о них стоит договориться.

camelCase для переменных, функций и свойств

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

const orderCount = orders.length;
const isLoggedIn = Boolean(session);
const user = { firstName: "Ada", lastLogin: null };

function calculateShipping(order) {
  // ...
}

Для булевых значений префикс is, has или can (isLoggedIn, hasAccess) отвечает на вопрос «истинно или ложно — что именно?» раньше, чем кто-нибудь успеет его задать. Руководство Google использует те же префиксы для булевых геттеров (isFoo, hasFoo).

У руководства Google есть и хорошее правило о длине имён: делайте имя настолько описательным, насколько это разумно, и не сокращайте, выбрасывая буквы. errorCount и customerId подходят, nErr и cstmrId — нет. Исключение — переменная, которая видна не больше десяти строк, например i в коротком цикле или x в points.map((x) => x * 2).

Коллекции, счётчики и обработчики событий

Несколько приёмов делают имена предсказуемыми. Для массива берите множественное число (users), для одного элемента — единственное, чтобы for (const user of users) читалось естественно. Объект для поиска называйте по его ключу (usersById), а число — по тому, что оно считает (userCount). Для событий в React есть распространённое соглашение, описанное в документации: обработчики называют handle плюс событие (handleClick), а пропсы, принимающие обработчик, начинаются с on и заглавной буквы (onClick, onSave).

PascalCase для классов, конструкторов и компонентов

PascalCase получает всё, что вы создаёте через new, а также всё, что React отрисовывает как компонент:

class ShoppingCart {
  addItem(item) {
    // ...
  }
}
const cart = new ShoppingCart();

function OrderSummary({ order }) {
  return <section>{order.id}</section>;
}

Для классов это соглашение. Для React — требование: в документации React сказано, что имя компонента должно начинаться с заглавной буквы, иначе он не заработает, потому что JSX считает теги со строчной буквы, такие как <section>, обычными HTML-элементами. В TypeScript тот же PascalCase используют для интерфейсов, псевдонимов типов и перечислений.

Встроенные объекты показывают всё это на примере. Конструкторы пишутся в PascalCase (Map, Promise, Date), методы — в camelCase (Object.fromEntries, Array.prototype.flatMap), а неизменные значения — заглавными буквами (Math.PI, Number.MAX_SAFE_INTEGER).

Когда использовать CONSTANT_CASE (и когда нет)

Больше всего путаницы вызывают константы, потому что const не означает «константа». Он лишь не даёт переприсвоить переменную, а массив или объект, на который она указывает, по-прежнему может измениться. Оба крупных руководства оставляют заглавные буквы для чего-то более строгого:

  • Google: CONSTANT_CASE — для значений уровня модуля (и статических свойств), которые глубоко неизменяемы. Одного намерения никогда не менять объект недостаточно, а константы, объявленные внутри функций, остаются в lowerCamelCase.
  • Airbnb: заглавные необязательны и нужны только для значения, которое экспортируется, объявлено через const и гарантированно никогда не изменится (включая вложенные свойства). Писать заглавными каждый const внутри файла не нужно.
// Module level, never changes: capitals
export const MAX_UPLOAD_BYTES = 5 * 1024 * 1024;
export const ORDER_STATUS = Object.freeze({ PENDING: "pending", SHIPPED: "shipped" });

function visibleNames(items) {
  const visible = items.filter((item) => !item.hidden); // const, but not a constant
  const names = [];                                     // const binding, mutable array
  for (const item of visible) names.push(item.name);
  return names;
}

В одном вопросе руководства расходятся: как писать ключи объекта, который используется как перечисление. Google пишет члены перечисления в CONSTANT_CASE, как выше; Airbnb предпочитает ключи строчными буквами внутри экспорта, имя которого записано заглавными. Подойдёт любой вариант, если вся кодовая база следует одному. И ещё одна оговорка: Object.freeze работает поверхностно, поэтому в замороженном объекте всё равно может лежать вложенный объект, который меняется.

Приватные поля: используйте #, а не подчёркивание

Старая привычка помечать «приватные» члены подчёркиванием в начале (this._count) — всего лишь подсказка: свойство по-прежнему может прочитать и записать кто угодно. Именно поэтому руководство Airbnb советует не использовать подчёркивания в начале и в конце имён, а руководство Google по TypeScript тоже запрещает их как префиксы и суффиксы. В современном JavaScript есть настоящие приватные поля:

class Counter {
  #count = 0;
  increment() {
    return ++this.#count;
  }
}

const counter = new Counter();
counter.increment();
counter.count;   // undefined
// counter.#count  // SyntaxError outside the class

Аббревиатуры: userId или userID?

Правило Google — обращаться с аббревиатурами как с обычными словами: customerId, xmlHttpRequest, loadHttpUrl. Правило Airbnb обратное: аббревиатура остаётся целиком заглавной или целиком строчной, как в SMSContainer или httpRequests. Сам браузер определиться не может: XMLHttpRequest, innerHTML, getElementById и encodeURIComponent соседствуют в одной платформе.

Мы бы выбрали правило Google: оно предсказуемо, сохраняет видимыми границы слов и чисто преобразуется из одного стиля в другой. Но единообразие важнее самого выбора. Правила обращения с аббревиатурами во всех крупных руководствах мы сравниваем в статье camelCase и PascalCase.

Как обращаться с данными в snake_case из API

API, за которыми стоит Python, Ruby или база данных SQL, часто отдают ключи вроде first_name. Не давайте им расползтись по вашему коду. Переименовывайте в том месте, где данные поступают, а деструктуризация делает это одной строкой:

const response = await fetch("/api/users/42");
const { first_name: firstName, last_login: lastLogin } = await response.json();

Для целых ответов напишите по небольшой функции на каждое направление, которая рекурсивно преобразует ключи, и вызывайте её в клиенте API; одну такую мы разбираем в статье о преобразовании ключей JSON из camelCase в snake_case. Для разового переименования наш конвертер регистра превратит last_login в lastLogin одним щелчком.

Следите за соглашениями с помощью ESLint

Два базовых правила ESLint покрывают большую часть сказанного. camelcase отмечает подчёркивания в середине имён, но не трогает константы из одних заглавных букв и подчёркивания в начале или в конце. new-cap отмечает new с именем, начинающимся со строчной буквы, и функцию с заглавной буквы, вызванную без new.

// eslint.config.js
export default [
  {
    rules: {
      camelcase: ["error", { properties: "never" }],
      "new-cap": "error",
    },
  },
];

Опция properties: "never" не даёт camelcase придираться к полям API вроде data.first_name, но объявляемые вами переменные по-прежнему проверяются. В проектах на TypeScript правило @typescript-eslint/naming-convention идёт дальше: оно позволяет задать допустимый формат для каждого вида идентификаторов — переменных, функций, классов, параметров типов и так далее. Полный набор правил Google есть в Google JavaScript Style Guide.

Чек-лист по именованию переменных в JavaScript

  • camelCase — для переменных, функций, параметров, методов и свойств.
  • PascalCase — для классов, конструкторов, компонентов React, а также типов, интерфейсов и перечислений TypeScript.
  • CONSTANT_CASE — для значений уровня модуля, которые никогда не меняются, а не для каждого const.
  • Поля #private вместо префиксов с подчёркиванием.
  • Одно правило для аббревиатур, записанное в руководстве для контрибьюторов.
  • Ключи API в snake_case преобразуются один раз, на границе.