Перейти к основному содержимому

Логические присваивания

· 4 мин. чтения
Шу-Ю Го ([@_shu](https://twitter.com/_shu))

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

Что до сих пор отсутствовало, так это возможность комбинировать логические операции с присваиванием. Но теперь это изменилось! JavaScript теперь поддерживает логические присваивания с помощью новых операторов &&=, ||= и ??=.

Операторы логического присваивания

Прежде чем углубиться в новые операторы, давайте освежим в памяти существующие операторы составного присваивания. Например, значение lhs += rhs приблизительно эквивалентно lhs = lhs + rhs. Это приближение справедливо для всех существующих операторов @=, где @ заменяет бинарный оператор, такой как + или |. Стоит отметить, что это строго верно только тогда, когда lhs является переменной. Для более сложных выражений на левой стороне, таких как obj[computedPropertyName()] += rhs, левая сторона оценивается только один раз.

Теперь давайте углубимся в новые операторы. В отличие от существующих операторов, lhs @= rhs примерно не означает lhs = lhs @ rhs, когда @ является логической операцией: &&, ||, или ??.

// Для дополнительного обзора, вот семантика логического "и":
x && y
// → y, если x истинно
// → x, если x ложно

// Во-первых, логическое "и" с присваиванием. Две строки после этого
// блока комментариев эквивалентны.
// Обратите внимание, что, как и существующие операторы составного присваивания,
// более сложные левые стороны оцениваются только один раз.
x &&= y;
x && (x = y);

// Семантика логического "или":
x || y
// → x, если x истинно
// → y, если x ложно

// Аналогично, логическое "или" с присваиванием:
x ||= y;
x || (x = y);

// Семантика оператора объединения nullish:
x ?? y
// → y, если x является null или undefined
// → x, если x не является null или undefined

// Наконец, присваивание с объединением nullish:
x ??= y;
x ?? (x = y);

Семантика короткой схемы

В отличие от их математических и побитовых аналогов, логические присваивания следуют поведению короткой схемы своих логических операций. Они только выполняют присваивание, если логическая операция оценивает правую сторону.

Сначала это может показаться запутанным. Почему бы не присваивать безусловно, как в других составных присваиваниях?

Существует практическая причина для этого отличия. При комбинировании логических операций с присваиванием, присваивание может вызывать побочный эффект, который должен происходить условно, исходя из результата логической операции. Безусловное вызов побочного эффекта может негативно повлиять на производительность или даже корректность программы.

Давайте конкретизируем это с помощью примера двух версий функции, которая устанавливает сообщение по умолчанию в элемент.

// Отобразить сообщение по умолчанию, если оно не перекрывает ничего.
// Присваивание innerHTML происходит только если оно пустое.
// Не приводит к потере фокуса внутренними элементами msgElement.
function setDefaultMessage() {
msgElement.innerHTML ||= '<p>Нет сообщений<p>';
}

// Отобразить сообщение по умолчанию, если оно не перекрывает ничего.
// Баг! Может приводить к потере фокуса внутренними элементами msgElement
// каждый раз, когда оно вызывается.
function setDefaultMessageBuggy() {
msgElement.innerHTML = msgElement.innerHTML || '<p>Нет сообщений<p>';
}
примечание

Примечание: Поскольку свойство innerHTML определено как возвращающее пустую строку вместо null или undefined, необходимо использовать ||= вместо ??=. При написании кода, имейте в виду, что многие веб-API не используют null или undefined для обозначения пустоты или отсутствия значения.

В HTML присваивание свойству .innerHTML элемента является разрушительным. Внутренние дочерние элементы удаляются, а новые дочерние элементы, извлеченные из вновь присвоенной строки, вставляются. Даже если новая строка совпадает со старой, это вызывает дополнительную работу и потерю фокуса внутренними элементами. По этой практической причине, чтобы предотвратить нежелательные побочные эффекты, семантика операторов логического присваивания коротко замыкает присваивание.

Можно попытаться понять симметрию с другими операторами составного присваивания следующим образом. Математические и побитовые операторы безусловные, поэтому присваивание также безусловное. Логические операторы условные, поэтому присваивание также условное.

Поддержка логического присваивания