Как только вы просыпаетесь, начинается осознанный или неосознанный процесс принятия решений. Выключить будильник. Включить свет. Выглянуть из окна, чтобы проверить погоду. Почистить зубы. Надеть мантию и шляпу волшебника. Посмотреть в календарь. В общем… вы меня поняли. К тому моменту, когда вы шагнете за порог, можно будет насчитать уже сотни принятых вами осознанных или неосознанных решений. Каждое из этих решений определенным образом повлияло на то, что вы в итоге станете делать.
Например, если бы на улице было холодно, вам пришлось бы решать, что надеть: худи или куртку. На рис. 4.1 показано, как формируется это решение.
Рис. 4.1. Моделирование решений
На каждой стадии принятия решения вы задаете себе вопрос, на который можно дать ответ true (верно) или false (неверно). Ответ на этот вопрос определяет ваш следующий шаг и то, что в итоге вы наденете: футболку, худи или куртку. В более широком смысле любое наше решение можно смоделировать в виде череды инструкций true и false. От этого может стать слегка не по себе (еще бы!), но в общем и целом именно так мы, наше окружение и большинство живых существ совершаем свой выбор.
Такое обобщение особенно применимо ко всему, что делает наш компьютер. На примере всех кодов, которые мы успели написать, это не сразу бросается в глаза, но мы скоро исправим эту ситуацию. В этом уроке мы рассмотрим условные выражения. Они являются цифровым эквивалентом решений в ситуациях, где код производит некоторое действие в зависимости от того, оказывается что-либо true или false.
Поехали!
Инструкция if…else
Самая распространенная условная инструкция, используемая в коде, — это инструкция if…else, или просто инструкция if. Принцип ее работы показан на рис. 4.2.
Рис. 4.2. Как работает инструкция if
Чтобы в этом разобраться, рассмотрим инструкцию if…else в действии. Создайте новый HTML-документ и добавьте в него следующие разметку и код:
let safeToProceed = true;
if (safeToProceed) {
alert("You shall pass!");
} else {
alert("You shall not pass!");
}
Сохраните этот документ под именем if_else.htm и выполните его предпросмотр в браузере. Если все сработает как надо, вы увидите уведомление с текстом You shall pass! (рис. 4.3).
Рис. 4.3. Вы увидите это уведомление
За полученный результат отвечают следующие строки кода:
let safeToProceed = true;
if (safeToProceed) {
alert("You shall pass!");
} else {
alert("You shall not pass!");
}
Наше выражение (то, что следует за ключевым словом if и в итоге оценивается как true или false) — это переменная safeToProceed. Эта переменная инициализирована как true, следовательно, был задействован вариант true инструкции if.
Теперь замените значение переменной safeToProceed с true на false:
let safeToProceed = true;
if (safeToProceed) {
alert("You shall pass!");
} else {
alert("You shall not pass!");
}
На этот раз при запуске кода вы увидите уведомление с текстом You shall not pass! так как теперь выражение вычисляется как false (рис. 4.4).
Рис. 4.4. Уведомление, получаемое, когда выражение вычисляется как false
Пока что все это может казаться скучным, но в основном потому, что мы еще не добавляли элемент сложности, чтобы рассмотреть более жизненные сценарии. Это ждет нас на следующем этапе, когда мы глубже погрузимся в эту тему.
Встречайте: условные операторы!
В большинстве случаев наше выражение не будет простой переменной со значением true или false, подобно предыдущему примеру. В выражениях будут задействованы так называемые условные операторы, помогающие сравнивать два или больше двух выражений для утверждения результата true или false.
В общих чертах подобные выражения показаны на рис. 4.5.
if (expression operator expression) {
do_something;
} else {
do_something_different;
}
Рис. 4.5. Общий формат выражений с условными операторами
Оператор (то есть условный оператор) определяет связь между выражениями. Конечная цель — вернуть результат true или false, чтобы наша инструкция if понимала, какой блок кода выполнять. Ключом к выполнению всех этих действий являются сами условные операторы, которые приведены в табл. 4.1.
Табл. 4.1. Операторы
Оператор
Если true
= =
Если первое выражение дает результат, равный второму выражению
>=
Если первое выражение дает результат, который больше или равен второму выражению
>
Если первое выражение дает результат больше, чем второе выражение
<=
Если первое выражение дает результат, меньший или равный второму выражению
<
Если первое выражение дает результат меньше, чем второе выражение
!=
Если первое выражение дает результат, не равный второму выражению
&&
Если и первое, и второе выражения дают результат true
| |
Если либо первое, либо второе выражение дает результат true
Теперь перейдем от обобщенного понимания условных операторов к более конкретному, рассмотрев еще один пример, в котором подсвечен интересующий нас if-сегмент кода:
let speedLimit = 55;
function amISpeeding(speed) {
if (speed >= speedLimit) {
alert("Yes. You are speeding.");
} else {
alert("No. You are not speeding. What's wrong with you?");
}
}
amISpeeding(53);
amISpeeding(72);
Разберемся, что же именно здесь происходит. У нас есть переменная speedLimit, инициализированная как 55. Затем есть функция amISpeeding, получающая аргумент speed. Внутри нее инструкция if, чье выражение проверяет, является ли полученное значение speed большим или равным (привет, условный оператор >=) значению, содержащемуся в переменной speedLimit:
function amISpeeding(speed) {
if (speed >= speedLimit) {
alert("Yes. You are speeding.");
} else {
alert("No. You are not speeding. What's wrong with you?");
}
}
Последнее, что делает код, — это вызывает функцию amISpeeding, передавая ей два значения speed:
amISpeeding(53);
amISpeeding(72);
Когда мы называем эту функцию со скоростью 53, выражение speed >= speedLimit вычисляется как false. Так происходит, потому что 53 не больше и не равно значению, сохраненному в speedLimit, а именно 55. В итоге будет выводиться уведомление о том, что вы не превышаете скорость (No. You are not speeding…).
Противоположное происходит, когда мы вызываем amISpeeding со скоростью 72. В этом случае мы превышаем скорость и выражение вычисляется как true с последующим появлением соответствующего уведомления.
Создание более сложных выражений
Про выражения следует знать, что они могут быть такими сложными или простыми, какими вы их сами сделаете. Они могут состоять из переменных, вызовов функций или одних значений. Они даже могут быть сделаны из сочетаний переменных, вызовов функций или голых значений, разделенных с помощью любых из описанных выше операторов. Важно сделать так, чтобы в итоге выражение вычислялось как true или false.
Привожу чуть более сложный пример:
let xPos = 300;
let yPos = 150;
function sendWarning(x, y) {
if ((x < xPos) && (y < yPos)) {
alert("Adjust the position");
} else {
alert("Things are fine!");
}
}
sendWarning(500, 160);
sendWarning(100, 100);
sendWarning(201, 149);
Обратите внимание на то, как выглядит условие внутри инструкции if, принадлежащей функции sendWarning:
function sendWarning(x, y) {
if ((x < xPos) && (y < yPos)) {
alert("Adjust the position");
} else {
alert("Things are fine!");
}
}
В данном случае было выполнено три сравнения. Во-первых, меньше ли x, чем xPos. Во-вторых, меньше ли y, чем yPos. И в-третьих — проверка, не расценивается ли первая и вторая инструкции как true, чтобы оператор && мог также вернуть это значение. Можно соединять в цепочки множество условных инструкций в зависимости от того, что мы делаем. Помимо запоминания действий самих операторов может быть непросто проследить, чтобы все условия и подусловия были правильно изолированы скобками.
Весь рассматриваемый материал из текущего и предыдущего разделов подпадает под общее определение бинарной логики. Если вы ничего не знаете по этой теме, я рекомендую прочитать прекрасную статью о режимах совместимости.
Варианты инструкций if…else
Мы почти закончили с инструкцией if. Осталось лишь разобраться с ее «родственниками».
Одиночная инструкция if
Первый вариант — это одиночная инструкция if без родственника else:
if (weight > 5000) {
alert("No free shipping for you!");
}
В этом случае если выражение вычисляется как true, то все отлично. Если как false, тогда код просто пропускает уведомление и переходит к выполнению следующих действий. При работе с инструкциями if блок else является опциональным. Чтобы составить контраст одиночной инструкции if, на помощь спешит ее родня.
Устрашающая инструкция if…else-if…else
Не все можно четко уложить в одиночную инструкцию if или if…else. В таких ситуациях можно использовать ключевое слово else if, чтобы создавать цепочки инструкций if. Не будем вдаваться в подробности, а просто посмотрим следующий пример:
if (position < 100) {
alert("Do something!");
} else if ((position >= 200) && (position < 300)) {
alert("Do something else!");
} else {
alert("Do something even more different!");
}
Если первая инструкция if будет вычислена как true, тогда обработка кода пойдет по ветке первого уведомления. Если первая инструкция окажется false, тогда обработка переходит к вычислению инструкции else if, проверяя, true она или false. Это будет продолжаться, пока код не будет обработан до конца. Другими словами, обработка нашего кода — это движение вниз через все инструкции if и else if, пока одно из выражений не будет вычислено как true:
if (condition) {
…
} else if (condition) {
…
} else if (condition) {
…
} else if (condition) {
…
} else if (condition) {
…
} else if (condition) {
…
} else {
…
}
Если ни одна из инструкций не имеет выражений, вычисляемых как true, то выполняется обработка кода внутри блока else (если таковой существует). Если блока else нет, тогда выполнение просто перейдет к следующей части кода, находящейся за пределами инструкций if. С помощью более сложных выражений и инструкций if…else if вы можете выразить практически любое решение, которое потребуется вычислить в коде.
Фух
Теперь вы знаете все, что нужно знать об инструкции if. Пришло время познакомиться с совершенно иным видом условных инструкций…
Инструкция switch
В мире программирования, наполненном прекрасными инструкциями if, else и else if, потребность в ином виде взаимодействий с условными инструкциями может отсутствовать. Но суровые люди, писавшие код на машинах размером с комнату и не боявшиеся волков в заснеженных горах, не согласились бы с этим. Поэтому теперь у нас есть так называемые инструкции switch. И прямо сейчас мы узнаем, зачем они нужны.
Использование инструкции switch
Не будем тянуть кота за хвост и сразу посмотрим на пример. Основа структуры инструкции switch выглядит так:
switch (expression) {
case value1:
statement;
break;
case value2:
statement;
break;
case value3:
statement;
break;
default:
statement;
break;
}
Всегда нужно помнить, что инструкция switch — это условная инструкция, проверяющая, является что-либоtrue или false, и не более того. Это что-либо, в свою очередь, является вариацией того, является ли результат вычисления выражения равным значению case. Чтобы прояснить этот момент, рассмотрим более подходящий пример:
let color = "green";
switch (color) {
case "yellow":
alert("yellow color");
break;
case "red":
alert("red color");
break;
case "blue":
alert("blue color");
break;
case "green":
alert("green color");
break;
case "black":
alert("black color");
break;
default:
alert("no known color specified");
break;
}
Здесь у нас есть переменная color, которой задано значение green:
let color = "green";
Мы также определяем переменную color в качестве выражения в инструкции switch:
switch (color) {
case "yellow":
alert("yellow color");
break;
case "red":
alert("red color");
break;
case "blue":
alert("blue color");
break;
case "green":
alert("green color");
break;
case "black":
alert("black color");
break;
default:
alert("no known color specified");
break;
}
Инструкция switch содержит коллекцию блоков case (случаев). При выполнении кода лишь один из этих блоков станет избранным. Выбор конкретного блока происходит путем сопоставления значения блока case с результатом вычисления выражения. В нашем случае, так как выражение вычисляется со значением green, будет выполнен код внутри блока case с тем же значением green:
switch (color) {
case "yellow":
alert("yellow color");
break;
case "red":
alert("red color");
break;
case "blue":
alert("blue color");
break;
case "green":
alert("green color");
break;
case "black":
alert("black color");
break;
default:
alert("no known color specified");
break;
}
Обратите внимание, что выполняется код, содержащийся только внутри блока case green. Так происходит благодаря ключевому слову break в конце этого блока. Когда выполнение кода достигает break, происходит выход из всего блока switch и код продолжает свое выполнение с участка, расположенного ниже. Если вы не указали ключевое слово break, то код продолжит выполняться внутри блока case green. Разница в том, что затем произойдет переход к следующему блоку case (в нашем примере black) и выполнению его кода. Таким же образом будут выполнены все последующие блоки case, если на пути не попадется другое ключевое слово break.
Таким образом, если вы запустите приведенный выше код, то увидите окно уведомления, как на рис. 4.6.
Рис. 4.6. Окно уведомления
Вы можете менять значение переменной color на другие допустимые значения, чтобы посмотреть, как выполняются другие блоки case. В некоторых случаях ни одно их значений блоков case не будет совпадать с результатом вычисления выражения. В таких ситуациях инструкция switch просто ничего не будет делать. Если вы захотите определить для нее поведение по умолчанию, добавьте блок default:
switch (color) {
case "yellow":
alert("yellow color");
break;
case "red":
alert("red color");
break;
case "blue":
alert("blue color");
break;
case "green":
alert("green color");
break;
case "black":
alert("black color");
break;
default:
alert("no known color specified");
break;
}
Обратите внимание, что блок default выглядит немного иначе, чем другие инструкции case. Фактически в нем просто отсутствует слово case.
Сходство с инструкцией if…else
Мы видели, что инструкция switch используется для вычисления условий — так же, как и инструкция if…else, на которую мы потратили уйму времени. Учитывая такой финт, давайте изучим этот момент подробнее и рассмотрим, как будет выглядеть инструкция if, если мы буквально переведем ее в инструкцию switch.
Допустим, есть такая инструкция if:
let number = 20;
if (number > 10) {
alert("yes");
} else {
alert("nope");
}
Так как переменная number имеет значение 20, инструкция if будет вычисляться как true. Выглядит достаточно просто. А теперь преобразуем ее в инструкцию switch:
switch (number > 10) {
case true:
alert("yes");
break;
case false:
alert("nope");
break;
}
Обратите внимание, что наше выражение — это number > 10. Значение case для блоков case установлено как true или false. Поскольку number>10 вычисляется как true, выполняется код внутри блока true. Несмотря на то что выражение в этом случае не было таким же простым, как считывание значения цвета из переменной в предыдущем разделе, на наш взгляд, принцип работы инструкции switch не изменился. Выражения могут быть настолько сложными, насколько вы пожелаете. Если они вычисляются во что-то, что может быть сопоставлено со значением блока case, тогда все в шоколаде.
Далее предлагаю рассмотреть чуть более сложный пример. Преобразуем уже рассмотренную инструкцию switch с переменной color в эквивалентные ей инструкции if…else. Первоначальная версия этой инструкции выглядит так:
let color = "green";
switch (color) {
case "yellow":
alert("yellow color");
break;
case "red":
alert("red color");
break;
case "blue":
alert("blue color");
break;
case "green":
alert("green color");
break;
case "black":
alert("black color");
break;
default:
alert("no color specified");
break;
}
Если преобразовать ее в череду инструкций if…else, она станет выглядеть так:
let color = "green";
if (color == "yellow") {
alert("yellow color");
} else if (color == "red") {
alert("red color");
} else if (color == "blue") {
alert("blue color");
} else if (color == "green") {
alert("green color");
} else if (color == "black") {
alert("black color");
} else {
alert("no color specified";
}
Как мы видим, инструкции if…else очень схожи с инструкциями switch, и наоборот. Блок default в этом случае становится блоком else, а связь между выражением и значением case инструкции switch объединена в условии if…else инструкции if…else.
Что же использовать
В предыдущем разделе мы увидели, насколько взаимозаменяемы инструкции switch и if…else. При наличии двух схожих способов выполнения каких-либо действий возникает естественное желание понять, когда лучше использовать один, а когда другой. Если кратко, то используйте тот, который вам больше нравится. В интернете много спорят о том, когда и какую из инструкций лучше использовать, но такие споры никогда не приводят к чему-то вразумительному.
Лично я предпочитаю использовать ту инструкцию, которая будет легче читаться. Если посмотреть на предыдущее сравнение инструкций if…else и switch, можно заметить, что при наличии большого количества условий инструкция switch выглядит немного чище. Она однозначно лаконичнее и читабельнее. Но только вам решать, какое количество условий определит ваш выбор использовать ту или иную инструкцию. Для меня обычно это четыре или пять условий.
Инструкция switch лучше работает, когда вы вычисляете выражение и сопоставляете результат со значением. Если вы выполняете более сложные действия с использованием странных условий, проверкой значения и т. д., вероятно, вы предпочтете использовать что-то другое. Кроме того, в ход могут пойти даже не инструкции if…else. Об альтернативах поговорим позже.
Подводя итог, останемся верны прежней рекомендации — использовать то, что нам больше нравится. Если вы являетесь членом команды, имеющей свои предпочтения по написанию кода, то следуйте им. В любом случае, что бы вы ни делали, будьте последовательны. Это облегчит не только вашу жизнь, но и жизнь тех, кто будет работать с вашим кодом. Кстати говоря, лично я никогда не имел дело с ситуациями, в которых мне пришлось бы использовать инструкцию switch. Ваш опыт все же может отличаться от моего.
КОРОТКО О ГЛАВНОМ
Несмотря на то что создание настоящего искусственного интеллекта выходит за рамки этой книги, вы можете писать код, помогающий приложениям принимать решения. Этот код почти всегда будет принимать форму инструкции if…else, в которой вы предоставите браузеру набор доступных для него выборов:
let loginStatus = false;
if (name == "Admin") {
loginStatus = true;
}
Эти выборы основываются на условиях, которые должны быть вычислены как true или false.
В этой главе мы изучили механику работы инструкций if…else и их кузена — инструкции switch. В будущих главах вы увидите, что мы будем часто взаимодействовать с этими инструкциями, как если бы они были нашими старыми друзьями. А к концу книги вы сдружитесь еще больше.
Если у вас есть какие-либо вопросы, касающиеся пройденного материала, не беспокойтесь. Обращайтесь с ними на форуме по адресу: https://forum.kirupa.com. Там вы получите быструю помощь от меня или других добрейших разработчиков.