IF Statement в JS
- понедельник, 7 апреля 2025 г. в 00:00:04
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('----');
}
Опираясь прямо на спецификацию ECMAScript, пусть есть:
if (exp) Statement else Statement
Как же он работает изнутри:
сначала вычисляется некоторое выражение exp которое находится в скобках.
expRef = Evaluation(exp)
результат этого вычисления сохраняется в expRef как ссылку на выражение.
далее преобразуется уже в булево значение
expVal = ToBoolean(GetValue(expRef))
через абстрактную операцию ToBoolean. Но перед этим идет процесс получения значения по ссылке expRef.
работает 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 состоит из:
[[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 внутри, для проверки доп. условий.
Пример кода:
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.