IF Statement в JS

от автора

Об IF-Statement (введение)

IF-Statement — это конструкция для некоторого условного выполнения кода. Она позволяет выполнить определенный блок кода в зависимости от того истинно или ложно некоторое условие и согласно официально спецификации ECMAScript, syntax if-statement можно записать как:

if (условие) действие if (условие) действие1 else действие2

То есть можно записать как с ELSE так и собственно без ELSE.

  • IF с английского языка переводится как «если».

  • ELSE с английского языка же переводится как «иначе» или еще говорят «в противном случае».

Примечание (dangling else)

Понятие Dangling ELSE (или как еще многие говорят висящий else) связано с неоднозначностью в интерпретации условных конструкций if-else, когда в коде есть вложенные условия, и неясно, к какому if относится блок else. Фактически все это сводится к проблеме синтаксиса, которая возникает из-за отсутствия явного указания границ блоков (то есть фигурных скобок {..} ) или неверного их оформления.

Пример dangling else:

let x = 5; if (x > 1)     if (x % 2 !== 0)         if (x !== undefined)              console.log(`x is ${x}`); else      console.log('x is not greater than 1');  if (x & 1)      if (x === undefined)         console.log(`(1) x is ${x}`); else     console.log('----');

На примере выше два блока кода if-else. Во-первых подумайте что выдаст нам такой код? Верно результат выполнения будет таким:

результат выполнения

результат выполнения

Из этого мини-анализа, можно понять что если писать код в таком виде, то else будет привязываться к ближайшему внутреннему if. То есть:

  1. В первом блоке else привязан к if (x !== undefined), а не к if (x > 1), как могло бы быть задумано.

  2. Во втором блоке else привязан именно уже к if (x === undefined), а не к if (x & 1).

Вы сразу вероятно зададите вопрос как решить проблему dangling else? Как на максимально себя обезопасить от этой заявленной проблемы? Все проще чем вы думаете. Просто пишите постоянно фигурные скобки {} — так вы более явно/четко обозначите границы внутренностей.

Вот полный, более правильный (модифицированный) код:

let x = 5; if (x > 1) {     if (x % 2 !== 0) {         if (x !== undefined) {             console.log(`x is ${x}`);         } else {             console.log(`x is undefined`);         }        } else {         console.log('x is even');     } } else {     console.log('----'); }      if (x & 1) {     if (x === undefined) {         console.log(`(1) x is ${x}`);     } else {         console.log('x is not undefined');      } } else {     console.log('----');  }     

IF-Statement в JS работа изнутри

Опираясь прямо на спецификацию ECMAScript, пусть есть:

if (exp) Statement else Statement

Как же он работает изнутри:

  • сначала вычисляется некоторое выражение exp которое находится в скобках.

    expRef = Evaluation(exp)

    результат этого вычисления сохраняется в expRef как ссылку на выражение.

  • далее преобразуется уже в булево значение
    expVal = ToBoolean(GetValue(expRef))

    через абстрактную операцию ToBoolean. Но перед этим идет процесс получения значения по ссылке expRef.

    работает ToBoolean очень просто:

    работа ToBoolean из спецификации

    работа ToBoolean из спецификации
    • Если переданный аргумент уже является Boolean, то сразу вернется этот аргумент.

    • Если аргумент один из так называемых Falsy Values (undefined, null, +-0, 0, NaN или пустая строка), то вернется false.

    • Во всех остальных случаях вернется true.

    после чего в expVal уже будет хранится либо true, либо false.

  • затем идет проверка условия

    • если условие истинно (то есть true), то будет выполнен первый Statement (то есть блок после if).

    • если условие ложно (то есть false) в таком случае будет выполнен уже второй Statement (то есть блок else).

    • причем результаты выполнения сохраняются как stmtCompletion

      stmtCompletion = Completion(Evaluation(Statement))

      • stmt — такое сокращение от «Statement» (оператор, утверждение). И во всей спецификации ECMAScript — такое понятие Statement — это фактически оператор.

      • А вот stmtCompletion это уже специальный объект типа Completion Record, который как раз представляет результат выполнения нашего Statement.

        из спецификации "Completion Record"

        из спецификации «Completion Record»

        Completion Record состоит из:

        • [[Type]] — это тип завершения (например normal, break, ..).

        • [[Value]]- возращаемое значение (empty, если ничего не возвращается).

        • [[Target]] — метка.

    • и в конце идет во

      Return ? UpdateEmpty(stmtCompletion, undefined)

      фактически возвращаем результат с гарантией, что там не empty (если пусто, то вернется undefined)

Примечание-2.

И вот как раз если блок ELSE отсутствует по каким-либо причинам, а условие при этом ложно, то результат вернет undefined.

А как же наше ELSE IF??

ELSE IF (как еще говорят «иначе если») — не существует как отдельная конструкция согласно спецификации. ELSE IF — это комбинация существующего ELSE и вложенного IF, и служит эта вся комбинация исключительно как возможность делать дополнительные условия. Можно еще сказать что ELSE IF — это некоторая цепочка else с новым if внутри, для проверки доп. условий.

Пример кода:

let x = 6;  if (x > 0) {     ++x;     console.log(x); } else if (x < 0) {     --x;     console.log(x); } else {     x *= 47127     console.log(x); }

Примечание-3.

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

Термин «опущенные фигурные скобки»

Без фигурных скобок следующая строка после if уже считается его телом, а остальные строки операторами.

И вот вам простой пример:

let x = 23714;  if (x > 0)     x += 34815;     x *= -1;  console.log(x); // -58529

Здесь истинное условие заставляет нас выполнить сложение с присваиванием, а затем идет умножение с присваиванием. Так вот *= -1 будет выполнятся всегда! А += 34815 только при истинности условия.

И более правильнее (если мы хотим чтобы *= выполнялось только при истинности условия) ставить фигурные скобки — разделяя логику — пример чуть модифицированный правда:

let x = 23714;  if (x < 0) {     x += 34815;     x *= -1; }      console.log(x); // 23714

Проблемы написания кода без фигурных скобок

  • dangling else

  • различные ошибки при добавлении новых строк кода

  • также снижение читаемости всего кода

  • и другие..

Замечание (и совет):

Лучше всегда ставить фигурные скобки {..} !!!

Нестандартная работа с логическими операторами

Раз уж упомянул их выше, давайте и тут быстренько разберемся. Как вы привыкли предсказывать куда указатель зайдет в if, или же в else? Допустим есть пример кода — вот такой:

if (1 && "abcd" && 12) {     console.log(1); } else {     console.log(2); }

С точки зрения большинства (а именно так думает практически 70-90% js-разработчиков, более точных данных у меня к сожалению нет), мы заходим в if, и тут получается 1 — это true, «abcd» — true, 12 — это тоже true -> значит true && true && true -> на выходе дает нам true. Ой как здорово у нас ИСТИНА, значит заходим в if, и выводим в консоль цифру 1.

НО

Все работает вообще не так! Вспоминаем работу IF-Statement.. да-да то что я описывал выше про Evaluation, про абстрактный ToBoolean и другое.

  • берется exp

  • вычисляется expRef

  • далее вычисляется expVal как результат от ToBoolean(GetValue(expRef))

Итак, работает все примерно так:

  • вычисляем какое у нас exp

  • начинаем слева 1 && «abcd»

    • по ToBoolean(1) —> true

    • по ToBoolean(«abcd») —> true

      и получается в голове у нас true && true — и согласно спецификации если слева стоит true, нужно вернуть правую часть. И возвращаем мы не true, а именно значение «abcd».

  • далее уже «abcd» && 12

    • ToBoolean(«abcd») —> true

    • ToBoolean(12) —> true

      слева снова стоит true возвращаем правую часть, получается 12.

  • таким образом, наш exp=12

  • ToBoolean(12) —> true — значит заходим в if.


ссылка на оригинал статьи https://habr.com/ru/articles/897884/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *