Короткие записи и порядок вычислений

Запись score = score + 5 («возьми счёт, прибавь 5, положи обратно») встречается так часто, что язык завёл сокращение: score += 5. Точно так же сокращаются и другие арифметические присваивания: -=, *=, /=, %= и **=.

Короткие записи

shorthand.jsjs
let score = 10;// то же, что score = score + 5score += 5;console.log(score);  // 15score -= 3;  // score = score - 3score *= 2;  // score = score * 2score /= 4;  // score = score / 4score %= 5;  // score = score % 5console.log(score);  // 1
Сокращение не меняет смысла – только запись: взять, изменить, положить обратно.

И одна оговорка, чтобы += не закрепился как чисто числовой: он работает и со строками – дописывает текст справа:

string-plus.jsjs
let text = 'Привет';text += '!';console.log(text);
Консоль
Привет!

А для самого частого случая – изменить ровно на единицу – есть записи ещё короче: score++ увеличивает на 1, score-- уменьшает. Их зовут инкрементом и декрементом. Инкремент считает накопленное (очки, верные ответы – от нуля вверх), декремент – оставшееся (попытки, секунды – от запаса к нулю).

incdec.jsjs
let correct = 0;// верный ответ – счёт растётcorrect++;correct++;console.log(correct);  // 2let attempts = 3;attempts--;  // попытка потраченаconsole.log(attempts);  // 2

Префикс и постфикс

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

Разница – в том, какое значение отдаёт сама запись. score++ – не только команда, но и выражение: у него есть значение, как у 2 + 3, – его можно напечатать или положить в переменную. Постфиксная форма сначала отдаёт старое значение и только потом меняет переменную. Префиксная – наоборот: сначала меняет, потом отдаёт новое.

Пока значение записи никто не читает, формы неотличимы: строка score++; и строка ++score; делают одно и то же. Разница проявляется, только когда вы одновременно меняете и читаете – кладёте запись в переменную или печатаете. Пройдите по шагам:

Постфикс score++ против префикса ++score
let score = 7;
Начинаем со счёта 7.
console.log(score++);
console.log(score);
console.log(++score);
console.log(score);
prefix-postfix.jsjs
let x = 5;// 5 – постфикс: сначала отдал старое, потом увеличилconsole.log(x++);// 6 – переменная уже измененаconsole.log(x);let y = 5;// 6 – префикс: сначала увеличил, потом отдал новоеconsole.log(++y);console.log(y);  // 6
Одна строка – одно правило: постфикс отдаёт старое значение, префикс – новое.

Лестница изученных операторов

Новые операторы встают на знакомую лестницу: счётчики и унарные – выше арифметики, присваивания – ниже всего. Это не полная таблица JavaScript – только лестница операторов, которые вы уже изучили:

Чем ниже ступень, тем ниже приоритет.

Проверим лестницу на двух ступенях: унарный минус срабатывает раньше умножения, а присваивание дожидается всю правую часть:

precedence.jsjs
// -6 – унарный минус сработал раньше умноженияconsole.log(-2 * 3);let total;// присваивание последним: сначала вся правая часть, 2 + 12 → 14total = 2 + 3 * 4;console.log(total);  // 14

Теперь соберём операторы урока в одну программу и пройдём её по шагам – видно, как лестница решает порядок на каждой строке:

Одна строка – три ступени лестницы
let score = 10;
В score10.
const bonus = 3;
score += bonus * 2 + 4;
score++;
Переменные
score = 10

Программа подлиннее – скидка, умножение и остаток в одной цепочке вычислений:

mix.jsjs
let price = 100;const qty = 3;price -= 10;  // скидка: в price уже 90// умножение раньше сложения: 270 + 50let total = price * qty + 50;total %= 100;  // 320 % 100 → 20console.log(total);
Консоль
20
Каждая строка опирается на лестницу: где-то сокращение, где-то приоритет умножения.

Скобки – верхняя ступень: всё внутри них считается первым, поэтому ими можно переписать любой порядок лестницы.

parens.jsjs
// 14 – умножение раньше сложенияconsole.log(2 + 3 * 4);// 20 – скобки переиграли порядокconsole.log((2 + 3) * 4);

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

middle.jsjs
// 18 – сначала 3 ** 2, потом умножение (не 36)console.log(2 * 3 ** 2);// 8 – остаток на ступени умножения: 10 - 2console.log(10 - 8 % 3);// 4 – деление раньше сложенияconsole.log(2 + 6 / 3);

Нижняя ступень – все присваивания разом, включая сокращённые. Правая часть всегда считается целиком до записи: price *= 2 + 3 умножает на 5, а не «умножает на 2 и прибавляет 3».

combined.jsjs
let price = 4;// сначала правая часть: 2 + 3 → 5, потом price * 5price *= 2 + 3;console.log(price);  // 20 – не 11
Сокращение раскрывается как price = price * (2 + 3) – со скобками вокруг правой части.

Направление вычислений: слева направо и справа налево

Приоритет решает, какой оператор сработает раньше. Но когда подряд стоят операторы одной ступени, нужен второй закон – направление. Большинство знакомых операторов группируются слева направо: 10 - 4 - 3 – это (10 - 4) - 3 → 3, а не 10 - (4 - 3) → 9.

left-to-right.jsjs
// 3 – слева направо: (10 - 4) - 3, не 10 - (4 - 3)console.log(10 - 4 - 3);// 1 – (20 / 4) / 5, а не 20 / (4 / 5)console.log(20 / 4 / 5);// 6 – минус и плюс на одной ступени: (7 - 3) + 2console.log(7 - 3 + 2);// '33' – сначала 1 + 2, потом склейка с '3'console.log(1 + 2 + '3');// '123' – склейка началась с первой же парыconsole.log('1' + 2 + 3);
Одна ступень – читается как написано, слева направо. Со строкой в цепочке порядок решает, где сложение станет склейкой.

Из уже изученного справа налево группируются три конструкции: возведение в степень, тернарный оператор и присваивания. У присваивания это видно в цепочке a = b = 5: сначала b = 5 – у этой записи, как и у счётчиков, есть собственное значение (только что записанная пятёрка), – и оно присваивается a: обе переменные получают 5. У степени – в двойной записи 2 ** 3 ** 2: сначала 3 ** 2 → 9, затем 2 ** 9 → 512, а не 8 ** 2 → 64.

Тернарник тоже связывается справа налево – ещё одна причина, по которой вложенные тернарники читаются с трудом.

right-to-left.jsjs
let a;let b;// сначала b = 5, его результат – в aa = b = 5;console.log(a, b);  // 5 5// 512 – сначала 3 ** 2 → 9, потом 2 ** 9console.log(2 ** 3 ** 2);
Присваивание и ** – два исключения: цепочка читается с правого края.

// теперь вы знаете

  1. Короткие записи += -= *= /= %= **= и счётчики ++/-- меняют переменную на месте
  2. Постфикс x++ отдаёт старое значение, префикс ++x – новое; в одиночной строке формы неотличимы
  3. Присваивания – нижняя ступень лестницы приоритетов: правая часть считается целиком
  4. Внутри ступени операторы группируются слева направо; степень, тернарник и присваивания – справа налево

Проверьте себя

В score лежит 7. Что окажется в переменной после score %= 4?

Что окажется в переменных после let x = 5; const y = x++;?

Чему равно 2 ** 3 ** 2?

В score лежит 4. Что будет после score *= 2 + 3?

переключают уроки · не работают при фокусе в полях ввода