Приоритет операторов и ассоциативность

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


Обзор ключевых уровней приоритета (от высшего к низшему)

Приоритет Операторы Ассоциативность
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')) { ... }

Рекомендации по читаемости кода

  1. Никогда не полагайтесь на память о приоритетах
    Даже опытные разработчики ошибаются. Если есть сомнения — используйте скобки.

  2. Разбивайте сложные выражения на части
    Вместо:

    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;
    
  3. Используйте переносы для многострочных условий

    if (user &&
        user.profile &&
        user.profile.isActive &&
        (user.role === 'admin' || user.role === 'editor')) {
      showDashboard();
    }
    
  4. Не пишите «умные» однострочные конструкции ради экономии строк
    Например:

    // ❌ Непонятно
    result = a && b ? c : d || e;
    
    // ✅ Ясно
    if (a && b) {
      result = c;
    } else {
      result = d || e;
    }
    
  5. Для логических выражений — комментируйте намерение

    // Разрешить доступ, если пользователь админ ИЛИ (зарегистрирован и активен)
    const canAccess = user.role === 'admin' || (user.registered && user.active);