Именование переменных в 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 преобразуются один раз, на границе.


