Операторы сравнения

Сравнение – это вопрос к данным: «пять больше трёх?», «пароли совпадают?». Ответ всегда один из двух – true или false, те самые булевы значения из главы 2.

Операторы порядка пишутся как в математике: > – больше, < – меньше, >= – больше или равно, <= – меньше или равно. Каждый сравнивает два значения и возвращает true или false – ответ можно напечатать, сохранить в переменную и использовать везде, где программа ждёт булево значение:

Вопросы с ответом да или нет

Результат любого сравнения – знакомое булево значение.

order.jsjs
console.log(5 > 3);  // true// false – 2 не больше и не равно 3console.log(2 >= 3);console.log(4 < 10);  // true// true – равенство «меньше или равно» засчитываетconsole.log(7 <= 7);const score = 82;// сохраняем булев результат сравнения в переменнуюconst passed = score >= 50;// true – с ним можно работать дальше, как с любым значениемconsole.log(passed);

А вот равенств в языке два – нестрогое == и строгое === (с парными неравенствами != и !==). Чем они различаются и какое выбирать – разберём в этом уроке, а пока запишем равенство так же, как в математике:

equals.jsjs
// true – значения совпадаютconsole.log(7 == 7);// true – «не равно» тоже вопросconsole.log(7 != 8);const pin = 2409;// true – ввод совпал с сохранённым пин-кодомconsole.log(pin == 2409);

Граница: >= и <=

Строгие > и < границу не включают, а >= и <= – включают. На числовой прямой разница видна сразу: точка на границе либо выколота, либо закрашена.

border-gt.jsjs
// false – 10 не больше 10console.log(10 > 10);// true – 10 равно 10, и >= это засчитываетconsole.log(10 >= 10);const age = 18;// true – 18 равно 18: условие «с 18 лет» выполненоconsole.log(age >= 18);// false – 18 не больше 18console.log(age > 18);

У < и <= всё зеркально – луч решений уходит влево от границы:

border-lt.jsjs
const boxes = 5;// false – 5 не меньше 5console.log(boxes < 5);// true – 5 равно 5, и <= это засчитываетconsole.log(boxes <= 5);

Один символ меняет смысл условия: «скидка от 1000 рублей» – это total >= 1000. Возьмёте > – и заказ ровно на тысячу останется без скидки.

Два равенства: == и ===

Нестрогое приводит типы перед сравнением, строгое сравнивает как есть.

Исторически первым в языке появилось нестрогое равенство ==. Если типы операндов разные, оно сначала приводит их к одному типу по собственным правилам – чаще всего преобразуется одна сторона – и только потом сравнивает. Исключение – пара null и undefined: они сравниваются без всякого приведения.

На практике это рождает результаты, которые не угадать: пустая строка оказывается равна нулю, false – строке '0'. Правила приведения у == свои собственные, они не совпадают с Number() дословно, и их приходится заучивать:

surprises.jsjs
// true – пустая строка приведена к нулюconsole.log('' == 0);// true – строка '7' приведена к числу 7console.log('7' == 7);// true – оба приведены к нулюconsole.log(false == '0');// false – у null своё особое правилоconsole.log(null == 0);// true – эта пара равна только друг другуconsole.log(null == undefined);
Правила у == свои и неочевидные. Они разбираются в главе о приведении и равенстве – но помнить их ради == в повседневном коде незачем.

Чем это опасно в настоящей программе? Приведение типов при == не бросает ошибку: программа не падает и ничего не сообщает – просто продолжает работать с неверным результатом. Например, данные из поля ввода: пользователь ничего не ввёл, у вас пустая строка ''. Сравнение '' == 0 даёт true – программа не отличает «ничего не ввели» от «ввели ноль», хотя это два разных случая:

empty-field.js
=== отличает пустой ввод от нуля
// поле пустое: пользователь ничего не ввёлconst field = '';// false – «ничего не ввели» и «ввели 0» различимыconsole.log(field === 0);
== смешивает пустой ввод и ноль
// поле пустое: пользователь ничего не ввёлconst field = '';// true – == привёл пустую строку к нулюconsole.log(field == 0);
Баг без сообщения об ошибке: при == пустой ввод неотличим от введённого нуля.

Решение в языке уже есть: строгое равенство ===. Оно ничего не приводит: типы разные – сразу false, разговор окончен. Ответ зависит только от того, что вы сравниваете: '5' === 5false, 5 === 5true. Пара к нему – строгое неравенство !==.

strict.jsjs
// false – строка не число, случаи различимыconsole.log('' === 0);// false – тип на местеconsole.log('7' === 7);// true – тот же тип, то же значениеconsole.log(7 === 7);// true – типы разные, значит строгого равенства нетconsole.log(2 !== '2');
Правило === одно и предсказуемое: типы разные – false, дальше сравниваются значения.

Правил приведения у == много, они неочевидны, и держать их в голове ненадёжно. Договоримся: в этом курсе – только === и !==. Так же пишут стайлгайды больших компаний, а линтеры – инструменты автоматической проверки кода – помечают == как ошибку.

Сравнение строк

Больше и меньше работают и с текстом – по номерам символов.

Знаки > и < работают не только с числами – но со строками принцип другой. Главное: внутри JavaScript у каждого символа – буквы, цифры, знака – есть свой числовой код. У «а» один код, у «б» – следующий, у заглавных – свои собственные. Строки сравниваются посимвольно слева направо по этим кодам:

СимволКод
'1'49
'2'50
'Б'1041
'а'1072
'б'1073
'н'1085
'т'1090
Фрагмент кодов из таблицы Unicode.

Сравнение идёт по парам символов, слева направо: первый символ левой строки против первого символа правой, второй против второго. Пока коды в паре совпадают, разбор переходит к следующей паре. Как только коды в паре разошлись, ответ определён: меньше та строка, у которой код в этой паре меньше. Остальные символы не читаются.

Проследите по шагам на паре 'банан' и 'бант' – подсвечена текущая пара символов:

Пара за парой: 'банан' против 'бант'
const a = 'банан';
const b = 'бант';
console.log(a < b);
Сравниваются две строки. Длина здесь ни при чём: разбор идёт по парам символов слева направо.
Консоль
Нет вывода в консоль.

Пограничные случаи

Пары могут кончиться раньше, чем нашлось различие – так бывает, когда одна строка целиком совпала с началом другой. Вот тут – и только тут – в дело вступает длина: короткая строка считается меньшей.

prefix.jsjs
// true – «кот» кончился, различий не нашлосьconsole.log('кот' < 'коты');// false – та же пара, обратный вопросconsole.log('коты' < 'кот');// true – пустая строка меньше любой непустойconsole.log('' < 'а');

Если строки совпали целиком, ни одна не меньше и не больше – строгие < и > отвечают false. Здесь и пригождаются <= и >=: равенство они засчитывают. А на вопрос «это одна и та же строка?» отвечает ===.

same.jsjs
// false – ни одна не меньшеconsole.log('кот' < 'кот');// true – равенство засчитаноconsole.log('кот' <= 'кот');// true – строки совпадают целикомconsole.log('кот' === 'кот');

Регистр меняет ответ: коды заглавных букв меньше кодов строчных – Б это 1041, а б – 1073. Поэтому слово с заглавной оказывается меньше слова со строчной, даже когда по алфавиту всё наоборот. Лечится приведением к одному регистру – методом toLowerCase() из урока «Строки».

case.jsjs
// true – 1041 меньше 1072console.log('Банан' < 'апельсин');// true – та же причина, а не алфавитconsole.log('Яблоко' < 'абрикос');// false – в одном регистре порядок привычныйconsole.log('банан' < 'апельсин');// false – регистр убрали, коды в одном регистреconsole.log('Банан'.toLowerCase() < 'апельсин');

У пробела код 32 – меньше любой буквы и любой цифры. Поэтому лишний пробел в начале меняет ответ, а данные из полей ввода перед сравнением обычно чистят методом trim(): он срезает пробелы по краям строки (подробно – в уроке «Методы строк»).

space.jsjs
// true – пробел (32) меньше буквы «к»console.log(' кот' < 'кот');// false – после чистки строки одинаковыconsole.log(' кот'.trim() < 'кот');

Из того же правила следует результат, который легко принять за ошибку: '2' > '12'true. Это не числа, а строки: сравнились коды первых символов, код «2» больше кода «1», и на этом разбор закончился. Нужно числовое сравнение – превратите строки в числа, унарный плюс из урока «Преобразования типов» как раз для этого.

string-numbers.jsjs
// true – это строки: сравнились коды «2» и «1»console.log('2' > '12');// false – унарные плюсы сделали числа, сравниваются числаconsole.log(+'2' > +'12');

Порядок как в словаре

Сравнение по парам символов – не выдумка языка: так устроен словарь и список контактов в телефоне. Отыскивая «Морозова», вы не считаете буквы в фамилиях, а открываете список на «М», потом смотрите вторую букву, третью – и останавливаетесь на первом расхождении.

Отсюда и порядок в списке: «Абрамов» стоит раньше «Мороза», хотя он длиннее – различие нашлось уже на первой букве. А «Мороз» раньше «Морозова» по другой причине: расхождений не нашлось, и первая фамилия просто кончилась. На этих двух правилах и держится сортировка списка по алфавиту.

contacts.jsjs
// true – «А» раньше «М», длина не участвуетconsole.log('Абрамов' < 'Мороз');// true – расхождений нет, «Мороз» кончился первымconsole.log('Мороз' < 'Морозов');
Так список контактов и выстраивается: сначала первая буква, и только при полном совпадении – длина.

Но «как в словаре» получается не всегда – даже внутри одного языка и одного регистра. Русская ё по коду стоит после я (1105 против 1103), поэтому «ёлка» окажется больше «яблока», хотя в словаре всё наоборот. Разные алфавиты тоже идут блоками: вся латиница расположена раньше кириллицы.

Когда порядок нужен человеческий – для списка на экране, – строки сравнивают методом localeCompare: он учитывает правила языка и отвечает не true/false, а числом: отрицательное – левая строка раньше, ноль – для порядка они одинаковы, положительное – раньше правая. Гарантируется только знак – не проверяйте результат на === -1. Локаль стоит передавать явно ('ru'), иначе поведение зависит от окружения.

locale.jsjs
// true – код «ё» (1105) больше кода «я» (1103)console.log('ёлка' > 'яблоко');const result = 'ёлка'.localeCompare('яблоко', 'ru');// true – по правилам языка «ёлка» раньшеconsole.log(result < 0);
Операторы сравнивают коды символов; порядок по алфавиту даёт localeCompare.

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

  1. Сравнения возвращают boolean: >, <, >=, <=, ===, !==
  2. >= и <= засчитывают равенство, строгие > и < – нет: от этого зависит граница условия
  3. === сравнивает без преобразования типов – используйте всегда
  4. == преобразует типы по собственным правилам – их результат трудно предсказать
  5. Строки сравниваются по парам символов слева направо: ответ определяет первая различающаяся пара
  6. Длина влияет только в одном случае: общие символы совпали и одна строка кончилась – она меньше
  7. Коды заглавных меньше строчных, а «ё» стоит после «я» – это порядок кодов, а не алфавит

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

Скидка действует «от 1000 рублей». Какая проверка ей соответствует?

Что выведут две строки?

console.log('лес' < 'лес');
console.log('лес' <= 'лес');

Что выведут две строки?

console.log('100' > '99');
console.log(+'100' > +'99');

Что выведет console.log('дом' < 'домик')? Впишите ответ словом.

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