Логические операторы

В JavaScript есть семь логических операторов:

  • || (ИЛИ)
    • ||= (Оператор логического присваивания ИЛИ)
  • && (И)
    • &&= (Оператор логического присваивания И)
  • ! (НЕ)
  • ?? (Оператор нулевого слияния)
    • ??= (Оператор нулевого присваивания)

Здесь мы рассмотрим первые пять, операторы ?? и ??= будут в следующей статье.

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

Давайте рассмотрим их подробнее.

|| (ИЛИ)

Оператор «ИЛИ» выглядит как двойной символ вертикальной черты:

Традиционно в программировании ИЛИ предназначено только для манипулирования булевыми значениями: в случае, если какой-либо из аргументов true, он вернёт true, в противоположной ситуации возвращается false.

В JavaScript, как мы увидим далее, этот оператор работает несколько иным образом. Но давайте сперва посмотрим, что происходит с булевыми значениями.

Существует всего четыре возможные логические комбинации:

alert( true || true );   // true
alert( false || true );  // true
alert( true || false );  // true
alert( false || false ); // false

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

Если значение не логического типа, то оно к нему приводится в целях вычислений.

Например, число 1 будет воспринято как true, а 0 – как false:

if (1 || 0) { // работает как if( true || false )
  alert( 'истинно!' );
}

Обычно оператор || используется в if для проверки истинности любого из заданных условий.

К примеру:

let hour = 9;

if (hour < 10 || hour > 18) {
  alert( 'Офис закрыт.' );
}

Можно передать и больше условий:

let hour = 12;
let isWeekend = true;

if (hour < 10 || hour > 18 || isWeekend) {
  alert( 'Офис закрыт.' ); // это выходной
}

ИЛИ "||" находит первое истинное значение

Описанная выше логика соответствует традиционной. Теперь давайте поработаем с «дополнительными» возможностями JavaScript.

Расширенный алгоритм работает следующим образом.

При выполнении ИЛИ || с несколькими значениями:

result = value1 || value2 || value3;

Оператор || выполняет следующие действия:

  • Вычисляет операнды слева направо.
  • Каждый операнд конвертирует в логическое значение. Если результат true, останавливается и возвращает исходное значение этого операнда.
  • Если все операнды являются ложными (false), возвращает последний из них.

Значение возвращается в исходном виде, без преобразования.

Другими словами, цепочка ИЛИ || возвращает первое истинное значение или последнее, если такое значение не найдено.

Например:

alert( 1 || 0 ); // 1 (1 - истинное значение)
alert( true || 'какая-то строка' ); // true

alert( null || 1 ); // 1 (первое истинное значение)
alert( null || 0 || 1 ); // 1 (первое истинное значение)
alert( undefined || null || 0 ); // 0 (поскольку все ложно, возвращается последнее значение)

Это делает возможным более интересное применение оператора по сравнению с «чистым, традиционным, только булевым ИЛИ».

  1. Получение первого истинного значения из списка переменных или выражений.

    Например, у нас есть переменные firstName, lastName и nickName, все они необязательные (т.е. они могут быть неопределенными или иметь ложные значения).

    Давайте воспользуемся оператором ИЛИ ||, чтобы выбрать ту переменную, в которой есть данные, и показать её (или «Аноним», если ни в одной переменной данных нет):

    let firstName = "";
    let lastName = "";
    let nickName = "Суперкодер";
    
    alert( firstName || lastName || nickName || "Аноним"); // Суперкодер

    Если бы все переменные были ложными, в качестве результата мы бы наблюдали "Аноним".

  2. Сокращённое вычисление.

    Ещё одной отличительной особенностью оператора ИЛИ || является так называемое «сокращённое вычисление».

    Это означает, что ИЛИ || обрабатывает свои операнды до тех пор, пока не будет достигнуто первое истинностное значение, и затем это значение сразу же возвращается, даже не затрагивая другие операнды.

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

    В приведенном ниже примере срабатывает только второй alert:

    true || alert("никогда не сработает");
    false || alert("сработает");

    В первой строке оператор ИЛИ || останавливает выполнение сразу после того, как сталкивается с истинным значением (true), поэтому сообщение не показывается.

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

||= (Логическое присваивание ИЛИ)

Новая возможность

Эта возможность была добавлена в язык недавно. В старых браузерах может понадобиться полифил.

Оператор логического присваивания ИЛИ ||= записывается как обычный ИЛИ || с добавлением символа присваивания =. Такая запись не случайна, так как результат выполнения данного оператора напрямую зависит от действий уже известного нам ||.

Вот его синтаксис:

Оператор ||= принимает два операнда и выполняет следующие действия:

  • Вычисляет операнды слева направо.
  • Конвертирует a в логическое значение.
  • Если a ложно, присваивает a значение b.

Концепция оператора ||= заключается в «сокращённом вычислении», принцип работы которого мы разобрали ранее.

Теперь давайте перепишем a ||= b под вид «сокращённого вычисления»:

Мы уже знаем, что ИЛИ || возвращает первое истинное значение, поэтому, если a является таковым, вычисление до правой части выражения не дойдёт.

Вот пример с очевидным использованием оператора ||=:

let johnHasCar = false;

johnHasCar ||= "У Джона нет машины!"; // то же самое, что false || (johnHasCar = "...")

alert( johnHasCar ); // "У Джона нет машины!"

…А здесь происходит преобразование к логическому значению:

let manufacturer = ""; // оператор ||= преобразует пустую строку "" к логическому значению false

manufacturer ||= "Неизвестный производитель"; // то же самое, что false || (manufacturer = "...")

alert( manufacturer ); // "Неизвестный производитель"

Оператор логического присваивания ИЛИ ||= – это «синтаксический сахар», добавленный в язык в качестве более короткого варианта записи if-выражений с присваиванием.

Мы можем переписать приведённые выше примеры с использованием обычного if:

let johnHasCar = false;

if (johnHasCar == false) {
  johnHasCar = "У Джона нет машины!";
}

alert(johnHasCar); // "У Джона нет машины!"

let manufacturer = "";

if (manufacturer == false) {
  manufacturer = "Неизвестный производитель";
}

alert(manufacturer); // "Неизвестный производитель"

&& (И)

Оператор И пишется как два амперсанда &&:

В традиционном программировании И возвращает true, если оба аргумента истинны, а иначе – false:

alert( true && true );   // true
alert( false && true );  // false
alert( true && false );  // false
alert( false && false ); // false

Пример с if:

let hour = 12;
let minute = 30;

if (hour == 12 && minute == 30) {
  alert( 'Время 12:30' );
}

Как и в случае с ИЛИ, любое значение допускается в качестве операнда И:

if (1 && 0) { // вычисляется как true && false
  alert( "не сработает, так как результат ложный" );
}

И «&&» находит первое ложное значение

При нескольких подряд операторах И:

result = value1 && value2 && value3;

Оператор && выполняет следующие действия:

  • Вычисляет операнды слева направо.
  • Каждый операнд преобразует в логическое значение. Если результат false, останавливается и возвращает исходное значение этого операнда.
  • Если все операнды были истинными, возвращается последний.

Другими словами, И возвращает первое ложное значение. Или последнее, если ничего не найдено.

Вышеуказанные правила схожи с поведением ИЛИ. Разница в том, что И возвращает первое ложное значение, а ИЛИ –  первое истинное.

Примеры:

// Если первый операнд истинный,
// И возвращает второй:
alert( 1 && 0 ); // 0
alert( 1 && 5 ); // 5

// Если первый операнд ложный,
// И возвращает его. Второй операнд игнорируется
alert( null && 5 ); // null
alert( 0 && "какая-то строка" ); // 0

Можно передать несколько значений подряд. В таком случае возвратится первое «ложное» значение, на котором остановились вычисления.

alert( 1 && 2 && null && 3 ); // null

Когда все значения верны, возвращается последнее

alert( 1 && 2 && 3 ); // 3

Приоритет оператора && больше, чем у ||

Приоритет оператора И && больше, чем ИЛИ ||, так что он выполняется раньше.

Таким образом, код a && b || c && d по существу такой же, как если бы выражения && были в круглых скобках: (a && b) || (c && d).

Не заменяйте if на || или &&

Иногда люди используют оператор И && как «более короткий способ записи if-выражения».

Например:

let x = 1;

(x > 0) && alert( 'x больше нуля!' );

Инструкция в правой части && будет выполнена только в том случае, если вычисление дойдет до нее. То есть, только если (x > 0) истинно.

Таким образом, мы имеем аналог для следующего кода:

let x = 1;

if (x > 0) alert( 'x больше нуля!' );

Несмотря на то, что вариант с && кажется более коротким, if более нагляден и, как правило, более читабелен. Поэтому мы рекомендуем использовать каждую конструкцию по назначению: использовать if, если нам нужно if, и использовать &&, если нам нужно И.

&&= (Логическое присваивание И)

Новая возможность

Эта возможность была добавлена в язык недавно. В старых браузерах может понадобиться полифил.

Оператор логического присваивания И &&= записывается как два амперсанда && и символ присваивания =.

Вот его синтаксис:

Принцип действия &&= практически такой же, как и у оператора логического присваивания ИЛИ ||=. Единственное отличие заключается в том, что &&= присвоит a значение b только в том случае, если a истинно.

Концепция оператора логического присваивания И &&= также основывается на «сокращённом вычислении»:

Пример использования:

let greeting = "Привет"; // строка непустая, поэтому будет преобразована к логическому значению true оператором &&=

greeting &&= greeting + ", пользователь!"; // то же самое, что true && (greeting = greeting + "...")

alert( greeting ) // "Привет, пользователь!"

Так как оператор логического присваивания И &&= также как и ||= является «синтаксическим сахаром», мы можем без проблем переписать пример выше с использованием привычного для нас if:

let greeting = "Привет";

if (greeting) {
  greeting = greeting + ", пользователь!"
}

alert( greeting ) // "Привет, пользователь!"

На практике, в отличие от ||=, оператор &&= используется достаточно редко – обычно, в комбинации с более сложными языковыми конструкциями, о которых мы будем говорить позже. Подобрать контекст для применения данного оператора – довольно непростая задача.

! (НЕ)

Оператор НЕ представлен восклицательным знаком !.

Синтаксис довольно прост:

Оператор принимает один аргумент и выполняет следующие действия:

  1. Сначала приводит аргумент к логическому типу true/false.
  2. Затем возвращает противоположное значение.

Например:

alert( !true ); // false
alert( !0 ); // true

В частности, двойное НЕ !! используют для преобразования значений к логическому типу:

alert( !!"непустая строка" ); // true
alert( !!null ); // false

То есть первое НЕ преобразует значение в логическое значение и возвращает обратное, а второе НЕ снова инвертирует его. В конце мы имеем простое преобразование значения в логическое.

Есть немного более подробный способ сделать то же самое – встроенная функция Boolean:

alert( Boolean("непустая строка") ); // true
alert( Boolean(null) ); // false

Приоритет НЕ ! является наивысшим из всех логических операторов, поэтому он всегда выполняется первым, перед && или ||.