Приоритет операторов и ассоциативность
Приоритет операторов определяет порядок, в котором выполняются операции в выражении, когда скобки не указаны. Ассоциативность — направление вычисления (слева направо или справа налево), если операторы имеют одинаковый приоритет.
Обзор ключевых уровней приоритета (от высшего к низшему)
| Приоритет | Операторы | Ассоциативность |
|---|---|---|
| 1 | (), [], . |
Слева направо |
| 2 | !, ~, ++, --, typeof, void, delete, унарные + и -, |
Справа налево |
| 3 | ** |
Справа налево |
| 4 | *, /, % |
Слева направо |
| 5 | +, - (бинарные) |
Слева направо |
| 6 | <<, >>, >>> |
Слева направо |
| 7 | <, <=, >, >=, in, instanceof |
Слева направо |
| 8 | ==, !=, ===, !== |
Слева направо |
| 9 | & |
Слева направо |
| 10 | ^ |
Слева направо |
| 11 | | | Слева направо |
| 12 | && |
Слева направо |
| 13 | || | Слева направо |
| 14 | ?? |
Слева направо |
| 15 | ?: (тернарный) |
Справа налево |
| 16 | =, +=, -=, *=, /=, %=, **=, &=, \|=, ^=, <<=, >>=, >>>=, ??= |
Справа налево |
🔹 Важно: Операторы одной группы имеют одинаковый приоритет и выполняются в соответствии с ассоциативностью.
Примеры без скобок: что происходит на самом деле
Пример 1: Умножение и сложение
let result = 5 + 3 * 2;
// Выполняется как: 5 + (3 * 2) → 5 + 6 → 11
Умножение (*) имеет более высокий приоритет, чем сложение (+) — поэтому умножение выполняется первым.
Пример 2: Логические операторы
let a = true;
let b = false;
let c = true;
let result = a || b && c;
// Выполняется как: a || (b && c) → true || (false && true) → true || false → true
Оператор && выше по приоритету, чем || — поэтому сначала вычисляется b && c.
Пример 3: Тернарный оператор
let x = 10;
let y = x > 5 ? "да" : x < 3 ? "нет" : "не знаю";
// Эквивалентно: x > 5 ? "да" : (x < 3 ? "нет" : "не знаю")
Тернарный оператор ?: ассоциативен справа налево, поэтому вложенные условия обрабатываются корректно.
Пример 4: Смешанные операторы присваивания
let a = 1, b = 2, c = 3;
a = b += c *= 2;
// Выполняется как: c *= 2 → c = 6; b += c → b = 8; a = b → a = 8
// Порядок: справа налево, потому что `=` и составные операторы присваивания ассоциативны справа
Как избежать ошибок с помощью скобок
Скобки — это не просто формальность. Они делают код предсказуемым и понятным даже для того, кто читает его впервые.
Плохо:
if (user.isAdmin && user.isActive || user.isGuest) { ... }
Что здесь реально проверяется?
→ (user.isAdmin && user.isActive) || user.isGuest
А если вы хотели:
→ user.isAdmin && (user.isActive || user.isGuest)?
Разница критична.
Хорошо:
if (user.isAdmin && (user.isActive || user.isGuest)) { ... }
— Чётко видно намерение.
Особенно важно при сочетании логических и сравнительных операторов:
// Без скобок — легко ошибиться
if (age >= 18 && role === 'admin' || role === 'moderator') { ... }
// Скобки спасают
if (age >= 18 && (role === 'admin' || role === 'moderator')) { ... }
Рекомендации по читаемости кода
-
Никогда не полагайтесь на память о приоритетах
Даже опытные разработчики ошибаются. Если есть сомнения — используйте скобки. -
Разбивайте сложные выражения на части
Вместо:const isValid = !isEmpty && (value.length >= 5 && value.includes("@")) || isTemp;Лучше:
const isEmpty = value.trim().length === 0; const hasAtSymbol = value.includes("@"); const meetsLength = value.length >= 5; const isValid = !isEmpty && (meetsLength && hasAtSymbol) || isTemp; -
Используйте переносы для многострочных условий
if (user && user.profile && user.profile.isActive && (user.role === 'admin' || user.role === 'editor')) { showDashboard(); } -
Не пишите «умные» однострочные конструкции ради экономии строк
Например:// ❌ Непонятно result = a && b ? c : d || e; // ✅ Ясно if (a && b) { result = c; } else { result = d || e; } -
Для логических выражений — комментируйте намерение
// Разрешить доступ, если пользователь админ ИЛИ (зарегистрирован и активен) const canAccess = user.role === 'admin' || (user.registered && user.active);