Об 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. То есть:
-
В первом блоке else привязан к if (x !== undefined), а не к if (x > 1), как могло бы быть задумано.
-
Во втором блоке 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 из спецификации -
Если переданный аргумент уже является 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 состоит из:
-
[[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/
Добавить комментарий