Управление потоком

Компьютерная программа подобна путешествию за вашими данными. На протяжении этого путешествия данные встречаются с опытом, который влияет на них, навсегда меняя их. Путь должен быть пройден, как и в любом путешествии. Есть несколько путей, по которым данные могут пройти в этом путешествии. Иногда данные идут по одному пути, иногда по другому. Конечная цель программы определяет путь данных. Вы хотите, чтобы ваши данные шли по правильному пути при создании программ. Вы хотите, чтобы он поворачивал влево или вправо, вверх или вниз, назад или ехал прямо, когда вы ему скажете. Это называется управлением потоком.

Условные

Условное - это развилка дорог (или многочисленные развилки). Ваши данные достигают условного оператора, который сообщает ему, куда идти. Простейшие условные операторы используют комбинацию операторов if со сравнением и логическими операторами (<, >, <=, >=, ==, ===, !=, !==, &&, ||) для направления трафика. Они используют ключевые слова if и else.

Давайте напишем код. Создайте файл с именем conditional.js со следующим содержимым:

// Run this code in your browser with an HTML file
let a = prompt('Enter a number');
if (a === '3') {
  console.log("a is 3");
} else if (a === '4') {
  console.log("a is 4");
} else {
  console.log("a is neither 3, nor 4");
}

Мы используем функцию «подсказки» браузера, чтобы получить число от пользователя — по крайней мере, три раза через conditional.js. Обновив окно браузера, вы можете повторно запустить код. Введите значение 3 в первый раз. Введите значение 4 еще раз. Введите любое другое значение в третий и последующие разы. Оператор === используется в этом коде, чтобы определить, равно ли входное значение «3» и, если нет, равно ли оно «4». Если значение равно «3», программа переходит к строке 6. Строка 8 выполняется, если значение равно «4». Если значение другое, выполняется строка 10. Как видите, мы можем регулировать поток программы, используя условные операторы в операторе if. Поскольку приглашение возвращает строковое значение, наши сравнения выполняются со строками.

Все приведенные ниже примеры являются допустимыми условиями JavaScript.

if (x === 3) {                          // Example 1
  console.log("x is 3");
}
if (x === 3) {                          // Example 2
  console.log("x is 3");
} else {
  console.log("x is NOT 3");
}
if (x === 3) console.log("x is 3");     // Example 3
if (x === 3)                            // Example 4
  console.log("x is 3");
if (x === 3)                            // Example 5
  console.log("x is 3");
else
  console.log("x is NOT 3");
if (x === 3) {                          // Example 6
  console.log('x is 3');
} else {
  if (x === 4) {
    console.log('x is 4');
  } else {
    console.log('x is NOT 3 or 4');
  }
}
if (x === 3) {                          // Example 7
  console.log("x is 3");
} else if (x === 4) {
  console.log("x is 4");
} else {
  console.log('x is NOT 3 or 4');
}

В примере 1 показан простейший оператор if: он включает одно условие (x === 3) и одно предложение — в данном случае блок, оператор или выражение — которое выполняется, когда условие истинно. Когда условие ложно, выполнение возвращается к первому оператору или выражению после оператора if, а не к выполнению кода в предложении. В примере 2 показано, как использовать другое предложение для обработки как истинных, так и ложных обстоятельств в одном и том же операторе if. Если условие истинно, выполняется код в предложении if (первый блок); если оно ложно, выполняется код в предложении else. Очень важно понимать, что предложение else является частью оператора if, а не отдельным оператором. Примеры 3, 4 и 5 демонстрируют, что в блоке нет необходимости, если предложение if или else содержит один оператор или выражение. Вы должны использовать фигурные скобки в блоке, если хотите выполнить много операторов или выражений в предложении. В противном случае вы можете оставить их. Однако эта практика может быть проблематичной. Взгляните на этот код:

if (x === 3)
  console.log('x is 3');
  console.log('x is an odd number');

Согласно отступу, программист ожидает, что строка 3 будет выполнена, когда x равно 3, но не когда это любое другое число. Строка 3 - нет. Однако часть оператора if. Это отдельная фраза, которая следует за оператором if. Хотя JavaScript допускает это, в большинстве случаев его лучше избегать. Блоки улучшают читабельность и надежность вашего кода. Оба примера 6 и 7 реагируют одинаково. Пример 6 демонстрирует последствия удаления блока, а пример 7 показывает результат использования вложенного оператора if в предложении else. Мы можем игнорировать нашу рекомендацию использовать блоки в операторах if здесь; в противном случае это один из случаев, когда желателен пропуск блока. Пример 7 легче понять и поддерживать, поскольку в нем отсутствует синтаксический беспорядок в виде дополнительных скобок и отступов.

Сравнения

Помните, что операторы сравнения возвращают логическое значение: true или false. Мы протестируем их в узле, чтобы увидеть, как они работают. Операнды оператора — это выражения или значения, которые он использует. Операнды, для сравнения, являются выражениями слева и справа от оператора. Сравнение на равенство x === y, например, использует оператор === с двумя операндами: x и y.

(===) Оператор идентификации, также известный как оператор строгого равенства, возвращает значение true, если операнды имеют одинаковый тип и значение, и значение false в противном случае.

> 5 === 5
= true
> 5 === 4
= false
> 'abc' === 'abc'
= true
> 'abc' === 'abcd'
= false
> 'abc' === 'aBc'
= false
> '5' === '5'
= true
> '5' === '6'
= false
> 5 === '5'
= false
> '' === 0
= false

Стоит отметить, что мы можем сравнивать строки на строгое равенство. Две строки должны иметь одинаковое значение, чтобы быть строго равными. Неважно, какое значение, просто обе строки имеют одинаковое значение. Строки не равны, если вообще есть разница. Эти последние два примера служат напоминанием о том, что два значения должны быть одного типа, чтобы быть равными. В результате строка «5» не соответствует числу 5. В результате сравнение некорректно. Кроме того, последний пример демонстрирует, что два разных «ложных» значения не эквивалентны.

(!==) Оператор строгого неравенства возвращает false, если операнды имеют одинаковый тип и значение; в противном случае верно. Важно помнить, что !== является инверсией ===: когда === возвращает true,!== возвращает false, и наоборот.

// Compare with the `===` examples.
> 5 !== 5
= false
> 5 !== 4
= true
> 4 !== 156
= true
> 'abc' !== 'def'
= true
> 'abc' !== 'aBc'
= true
> 5 !== '5'
= true

Как и в случае ===, мы можем сравнить два значения одного типа и получить удовлетворительные ответы для строгого неравенства. Возвращаемое значение истинно, если два значения относятся к разным видам.

(==) Подобно ===, это оператор нестрогого равенства, часто известный как оператор свободного равенства. Когда операнды имеют разные типы, == пытается привести один из операндов к типу другого перед их сравнением, а в некоторых случаях может привести к приведению обоих операндов. Результат истинен, когда окончательные значения совпадают; в противном случае оно ложно. Принуждение может иметь непредвиденные последствия. Например, когда мы используем == для сравнения числа 5 с текстом «5», мы получаем true; когда мы используем ===, мы получаем ложь.

// Compare with the `===` examples.
> 5 == 5
= true
> 5 == 4
= false
> 5 == '5'
= true
> '' == 0
= true

(!=) Оператор нестрогого неравенства, также известный как оператор свободного неравенства, аналогичен оператору !==. Когда операнды имеют разные типы, != пытается привести один из операндов к типу другого операнда перед их сравнением, а в некоторых случаях может привести к приведению обоих операндов. Результат будет ложным, если окончательные значения совпадают; в противном случае результат верен.

// Compare with the `==` and `!==` examples.
> 5 != 5
= false
> 5 != 4
= true
> 5 != '5'
= false
> '' != 0
= false

(‹) Оператор «меньше чем» возвращает истину, если значение левого операнда меньше, чем значение правого операнда, и ложь в противном случае.

> 4 < 5
= true
> 5 < 4
= false
> 5 < 5
= false
> "4" < "5"
= true
> "42" < "402"
= false
> "42" < "420"
= true
> "42" < 420
= true

Строковые примеры очень сложные! Убедитесь, что вы их понимаете. Сравнение строк производится посимвольно. JavaScript просматривает строки слева направо, ища первый символ, который отличается от своего аналога в другой строке. Когда он обнаруживает другой символ, он сравнивает его со своим аналогом и делает вывод на его основе. Если обе строки равны по длине вплоть до длины более короткой строки, как в предыдущем примере, более короткая строка считается меньшей, чем большая строка. Последний пример демонстрирует, что приведение происходит, когда вы используете два разных вида. В этом сценарии «42» преобразуется в число, что приводит к числовому сравнению. Не пытайтесь вспомнить это.

(›) Оператор «больше» возвращает значение «истина», если значение левого операнда больше, чем значение правого операнда, и «ложь» в противном случае.

// Compare with the `<` examples.
> 4 > 5
= false
> 5 > 4
= true
> 5 > 5
= false
> "4" > "5"
= false
> "42" > "402"
= true
> "42" > "420"
= false
> "42" > 420
= false

(‹=) Когда значение левого операнда меньше или равно значению правого операнда, оператор меньше или равно возвращает значение true; в противном случае возвращается ложь. Следует отметить, что = не является подходящим оператором сравнения. Конечно, оператор <= одинаково хорошо работает со строками.

// Compare with the `<` examples.
> 4 <= 5
= true
> 5 <= 4
= false
> 5 <= 5
= true

(›=) Оператор больше или равно возвращает истину, если значение левого операнда больше или равно значению правого операнда, и ложь в противном случае. Следует отметить, что =› не является правильным оператором сравнения. Конечно, оператор >= одинаково хорошо работает со строками.

// Compare with the `>` examples.
> 4 >= 5
= false
> 5 >= 4
= true
> 5 >= 5
= true

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

(!) Оператор not возвращает истину, если операнд ложен, и ложь, если он истинен. То есть он противоречит своему операнду. В отличие от других операторов! принимает только один операнд, который отображается справа от оператора.

> !true
= false
> !false
= true
> !(4 === 4)
= false
> !(4 !== 4)
= true

В этих случаях JavaScript сначала оценивает выражение справа, а затем применяет! к результату, отрицая его. Например, мы знаем, что 4 === 4 правильно, следовательно! (4 === 4) неверно.

(&&) Оператор and возвращает истину, когда оба операнда истинны, и ложь, когда любой из операндов ложен.

> true && true
= true
> true && false
= false
> false && true
= false
> false && false
= false
> (4 === 4) && (5 === 5)
= true
> (4 === 4) && (5 === 6)
= false
> (4 === 5) && (5 === 5)
= false
> (4 === 5) && (5 === 6)
= false

(||) Оператор or возвращает истину, если один из операндов истинен, и ложь, если оба операнда ложны.

> true || true
= true
> true || false
= true
> false || true
= true
> false || false
= false
> (4 === 4) || (5 === 5)
= true
> (4 === 4) || (5 === 6)
= true
> (4 === 5) || (5 === 5)
= true
> (4 === 5) || (5 === 6)
= false

Короткие замыкания

&& и || операторы используют механизм оценки короткого замыкания для оценки своих операндов. Рассмотрим эти два выражения:

> isRed(item) && isPortable(item)
> isGreen(item) || hasWheels(item)

Первое выражение возвращает true, когда объект красный и переносимый. Если одно из условий неверно, весь вывод должен быть неверным. В результате, если программа заключает, что элемент не красный, ей не требуется проверять, является ли он переносимым. JavaScript прекращает вычисление, как только обнаруживает, что элемент не красный, тем самым сокращая все выражение. Поскольку он уже знает, что вся фраза должна быть ложной, нет необходимости использовать isPortable(). Аналогично, вторая фраза возвращает true, если объект зеленый или имеет колеса. Когда любой критерий соблюдается, общий вывод должен быть выполнен. В результате, если программа делает вывод, что объект зеленый, ей не нужно проверять, есть ли у него колеса. Как только JavaScript обнаруживает, что элемент зеленый, он закорачивает все выражение. Вся фраза должна быть правдивой.

Правдивость

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

a = 5
if (a) {
  console.log("how can this be true?");
} else {
  console.log("it is not true");
}
b = 0
if (b) {
  console.log("how can this be true?");
} else {
  console.log("it is not true");
}

«Как это может быть правдой?» говорит первый пример. Первый записывает «это неправда», тогда как второй записывает «это неправда». Это связано с тем, что JavaScript заставляет значение 5 быть истинным, а значение 0 — ложным. Повторим еще раз: JavaScript может преобразовать любое значение в логическое значение. В результате любое выражение может быть использовано в условном выражении. Мы часто замечаем, что выражение оценивается как истинное или ложное. Мы можем даже написать код, который выглядит так:

let x;
if (x = 5) {
  console.log("how can this be true?");
} else {
  console.log("it is not true");
}

Приведенный выше код не проверяет, равен ли x 5. Вместо этого он устанавливает x равным 5, а затем оценивает возвращаемый результат присваивания (5) как логическое значение. Когда 5 встречается в логическом выражении, оно оценивается как истинное. Почему это так? Решение простое: при преобразовании значения в логическое значение JavaScript интерпретирует следующие значения как ложные:

  • false
  • Номер 0. Это включает в себя все 3 варианта нуля в JavaScript:
  • 0: обычное нулевое значение.
  • -0: Отрицательный ноль. Это математическая чепуха, но реальная вещь в JavaScript.
  • 0n: BigInt версия нуля.
  • Пустая строка ('')
  • undefined
  • null
  • NaN

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

В JavaScript правдивость весьма удобна; есть несколько случаев, когда вы хотите обрабатывать значения 0 (все три формы), '', undefined, null и NaN, как если бы они были ложными. Это облегчает понимание условных фраз, но также застает ничего не подозревающего программиста врасплох. Будьте осторожны, если вы знакомы с другим языком, в котором используются ложные значения; большинство языков не согласны с тем, какие значения являются ложными. Это постоянный источник разочарования для программистов, имеющих дело с несколькими языками.

Возвращаясь к примеру с if (x = 5). Помните, что x = 5 — это присваивание, когда вы читаете такой код. Это дает 5, что является правильным значением. Избегайте использования присваиваний в условных операторах: if (x = 5) и if (x == 5) на первый взгляд кажутся идентичными. Они имеют очень разные значения и приводят к совершенно разным результатам. Это делает код подозрительным: назначение может быть преднамеренным, но также может быть и ошибкой, а ошибки — это ошибки, ожидающие, чтобы навредить ничего не подозревающим. Хуже того, может прийти другой программист и ошибочно «исправить» код. Как вы помните, операторы && и || логические операторы используют оценку короткого замыкания. Эти операторы также работают с истинными и ложными значениями и могут выдавать истинные значения вместо логических значений. При использовании && и || возвращаемым результатом всегда является значение операнда, которое было вычислено последним:

> 3 && 'foo'  // last evaluated operand is 'foo'
= 'foo'
> 'foo' && 3  // last evaluated operand is 3
= 3
> 0 && 'foo'  // last evaluated operand is 0
= 0
> 'foo' && 0  // last evaluated operand is 0
= 0
> 3 || 'foo'  // last evaluated operand is 3
= 3
> 'foo' || 3  // last evaluated operand is 'foo'
= 'foo'
> 0 || 'foo'  // last evaluated operand is 'foo'
= 'foo'
> 'foo' || 0  // last evaluated operand is 'foo'
= 'foo'
> '' || 0     // last evaluated operand is 0
= 0

Рассмотрим следующее уравнение, которое дает результат, который является либо истинным, либо ложным, но не логическим значением:

let foo = null;
let bar = 'qux';
let isOk = foo || bar;

В этом коде для isOk установлено истинное значение «qux». В большинстве случаев с «qux» можно обращаться так, как если бы это было булевым значением true. Однако использование строкового значения, как если бы оно было логическим, — не самый логичный подход к разработке кода. Это может даже показаться другому кодировщику, ищущему дефект, ошибкой. Это может быть даже ошибкой в ​​некоторых необычных случаях. Вы можете решить эту проблему, используя оператор if или троичное выражение:

let isOk = (foo || bar) ? true : false;
let isOk;
if (foo || bar) {
  isOk = true;
} else {
  isOk = false;
}

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

let isOk = !!(foo || bar);

На самом деле такого понятия как !! в JavaScript. Наоборот, два! операторы подряд. Выражение !!a равносильно написанию !(!a). Внутренний! преобразует значение a в false, если оно истинно, и в true, если оно ложно. Внешний! затем переключается с истинного на ложное или с ложного на истинное. Наконец, вместо значения правдоподобия у нас есть логическое значение:

> !!3    // 3 is truthy, !3 is false, !false is true
= true
> !!''   // '' is falsy, !'' is true, !true is false
= false

Приоритет оператора

JavaScript использует набор правил приоритета для оценки выражений с многочисленными операторами и подвыражениями. Операции сравнения перечислены ниже в порядке от самого высокого приоритета (вверху) до самого низкого приоритета (внизу) (внизу).

  • <=, <, >, >= — Сравнение
  • ===, !==, ==, != - Равенство
  • && - Логическое И
  • || - Логическое ИЛИ

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

if (x || y && z) {
  // do something
}

Не будем принимать во внимание тот факт, что оба || и && пока являются операторами короткого замыкания. Поскольку && имеет приоритет над ||, программа сначала анализирует подвыражение y && z. Результат этой оценки затем используется для оценки x || результат. Чтобы переопределить приоритет, мы можем использовать круглые скобки: подвыражения в круглых скобках оцениваются перед выражениями без скобок на той же глубине в основном выражении (на данный момент не беспокойтесь о том, что означает глубина):

if ((x || y) && z) {
  // do something
}

В этом коде х || Сначала оценивается y, а затем результат && z. Это не то же самое, что фраза без скобок. Скобки помогают компьютеру и другим программистам понять ваши намерения; используйте круглые скобки в каждом выражении с двумя или более отдельными операторами. Скобки оцениваются JavaScript в стандартном алгебраическом порядке. Сначала он оценивает выражение в самом внутреннем наборе круглых скобок, а затем переходит к самой внешней части выражения. Он оценивает несколько заключенных в скобки подвыражений с одинаковой глубиной слева направо. Он оценивает окончательное значение выражения после вычисления выражений в скобках. Оценка короткого замыкания не изменяет правила приоритета, хотя может сбить вас с толку, если вы попытаетесь об этом подумать. Подождите немного позже, чтобы попытаться выяснить, как и почему. На данный момент имейте в виду, что, хотя ускоренная оценка может запретить JavaScript вычислять выражение справа от оператора, правила приоритета остаются неизменными.

Тернарный оператор

Тернарный оператор — это быстрый и простой способ выразить краткое, компактное и простое условие if/else. Он использует? и: символы и принимает три операнда (отсюда и термин «троичный»):

> 1 == 1 ? 'this is true' : 'this is not true'
= 'this is true'
> 1 == 0 ? "this is true" : "this is not true"
= 'this is not true'

Как это работает? JavaScript начинается с оценки первого операнда (сравнения). Если результат верен, JavaScript оценивает второй операнд и возвращает его значение. В противном случае он оценивает третий операнд и возвращает его значение (которое ложно). Тернарный оператор имеет значительное преимущество перед оператором if/else, поскольку вся структура представляет собой выражение. То есть мы можем рассматривать троичное выражение как значение, присваивая его переменной, передавая его в качестве аргумента и так далее. Поскольку if/else — это оператор, мы не можем присвоить его вывод переменной.

> let message = true ? 'this is true' : 'this is not true'
= undefined
> message
= 'this is true'
> console.log(false ? 'this is true' : 'this is not true')
this is not true
= undefined

Это невозможно с выражением if/else. Если вы не знаете, как это работает, поэкспериментируйте с этим в узле и протестируйте несколько дополнительных сценариев. Многократное воздействие и тестирование устанавливают знакомство быстрее, чем что-либо еще.

Заявление о переключении

Оператор switch — это последняя структура условного потока, которую мы рассмотрим. Оператор switch похож на оператор if, но имеет другой интерфейс. Для строгого равенства (как и в случае с оператором ===) он проверяет одно значение на множество значений, тогда как if может проверять несколько выражений с любым условием. Зарезервированные термины switch, case, default и break используются в операторах switch. Часто бывает проще продемонстрировать, чем рассказать, и оператор switch не является исключением. Сначала создайте файл с именем switch.js со следующим содержимым:

let a = 5;
switch (a) {
  case 5:
    console.log('a is 5');
    break;
  case 6:
    console.log('a is 6');
    break;
  default:
    console.log('a is neither 5, nor 6');
    break;
} // => a is 5
/* This example is functionally identical to the following if/else statement:*/
let a = 5;
if (a === 5) {
  console.log('a is 5');
} else if (a === 6) {
  console.log('a is 6');
} else {
  console.log('a is neither 5, nor 6');
} // => a is 5

Вы можете видеть, насколько они похожи и чем они отличаются. Оператор switch оценивает выражение a, сравнивает его значение со значением в первом соответствующем предложении, а затем выполняет операторы и выражения, связанные с этим предложением. Значение выражения в этом примере равно 5; таким образом, компьютер выполняет операторы и выражения, связанные с пунктом case 5:. Операторы и выражения предложения default: выполняются, когда выражение не соответствует ни одному из предложений case; он работает аналогично последнему оператору else в операторе if. В каждом сценарии оператор break имеет решающее значение. Выполнение «проваливается» к следующему пункту case, если нет перерыва.

let a = 5;
switch (a) {
  case 5:
    console.log('a is 5');
  case 6:
    console.log('a is 6');
  default:
    console.log('a is neither 5, nor 6');
} // => a is 5
  //    a is 6
  //    a is neither 5, nor 6

Это необычное и почти всегда негативное поведение. В большинстве случаев вы хотите избежать отказов, которые приводят к выполнению нескольких предложений case для одного значения. Код, который проскальзывает во многих подобных ситуациях, сомнительный по определению; похоже, что вы пренебрегли оператором break, что делает его недостатком, ожидающим своего появления. Даже если код правильный, он кажется неверным. Оно часто неверно. Это не значит, что провалы никогда не уместны. Они полезны в некоторых ситуациях. Например, предположим, что вы хотите выполнить идентичную операцию в двух или более случаях:

let a = 5;
switch (a) {
  case 5:
  case 6:
  case 7:
    // executed if a is 5, 6, or 7
    console.log("a is either 5, 6, or 7");
    break;
  case 8:
  case 9:
    // executed if a is 8 or 9
    console.log('a is 8 or 9');
    break;
  default:
    // executed if a is anything else
    console.log('a is not 5, 6, 7, 8, or 9');
    break;
}

Технически это ошибочный вариант, но поскольку в каждом случае используется одно предложение, его безопасно использовать, и он не намекает на потенциальную проблему. Операторы Switch имеют несколько применений. Это эффективные инструменты JavaScript. Если они вам не нравятся, потратьте некоторое время на настройку тех, которые мы показали выше, и посмотрите, как они реагируют на ваши изменения. Расширьте их возможности и откройте для себя их силы.