if, else if, else, and truthiness
A program that always does the same thing is a very expensive way to print a fixed message. The moment it has to react — pass or fail, open or closed, enough seats or not — you need a way to run one piece of code and skip another.
let marks = 44;
if (marks >= 50) {
console.log("Pass");
} else {
console.log("Fail");
}
Fail
The condition in the brackets produces a boolean, exactly as in
the equality lesson. If it is true,
the first block runs. Otherwise the else block does. Never both.
else if chains, and why order decides everything
For more than two outcomes you add else if between them. JavaScript checks
each condition in order and stops at the first one that is true.
let examMarks = 72;
if (examMarks >= 80) {
console.log("A");
} else if (examMarks >= 65) {
console.log("B");
} else if (examMarks >= 50) {
console.log("C");
} else {
console.log("Not passed");
}
B
72 is not 80 or more, so the first test fails. It is 65 or more, so the second
succeeds, B prints, and every remaining branch is skipped without being
looked at.
That "stops at the first true one" behaviour is the whole reason order matters, and getting it backwards produces a bug that never crashes:
let topMarks = 91;
if (topMarks >= 50) {
console.log("C");
} else if (topMarks >= 80) {
console.log("A");
}
C
91 deserves an A and receives a C. The >= 80 line can never run, because
anything that reaches 80 already passed the >= 50 test above it. Write
threshold chains from the most demanding condition down to the least, always.
The braces are not decoration
You are allowed to leave the curly braces off when the branch is a single statement. It reads fine and it sets a trap.
let raining = false;
if (raining)
console.log("Take the bus.");
console.log("Carry an umbrella.");
Carry an umbrella.
The indentation says both lines belong to the if. JavaScript disagrees:
without braces only the first statement is controlled, and the second one
runs unconditionally. It rained on nobody and the umbrella advice came out
anyway.
This bug survives code review because the code looks correct. Put the braces
on every if, including one-liners, and it cannot happen.
Truthy and falsy
The condition does not have to be a boolean. Give if any value and JavaScript
converts it to true or false first. Values that convert to false are
called falsy, and there are only a handful of them.
console.log(Boolean(0));
console.log(Boolean(""));
console.log(Boolean(null));
console.log(Boolean(undefined));
console.log(Boolean(NaN));
console.log(Boolean(false));
false false false false false false
That is the complete list for everyday purposes: zero, the empty string,
null, undefined, NaN and false itself. Everything else is truthy —
every non-empty string, every non-zero number including negatives, every array,
every object.
console.log(Boolean("0"));
console.log(Boolean("false"));
console.log(Boolean(-1));
console.log(Boolean(" "));
console.log(Boolean([]));
true true true true true
The string "false" is truthy, because it is a five-character string and
JavaScript is not reading English. A single space is truthy for the same
reason. An empty array is truthy, even though an empty string is not.
if (count) does not ask whether count exists
Here is the mistake, and it is a good one because the wrong version works almost all the time.
You want to show a score only when there is a score to show, so you write
if (goalsScored). It reads like "if there is a goals figure". It is not what
it means.
let goalsScored = 0;
if (goalsScored) {
console.log("Bilal scored " + goalsScored + " goals.");
} else {
console.log("No score recorded for Bilal.");
}
No score recorded for Bilal.
Bilal played. His score was recorded. It was zero, and zero is falsy, so the program announced that no score exists. Test it with 2 goals and it works. Test it with 5 and it works. The only failing case is the one nobody thinks to try, and it will reach production.
The same applies to the empty string. A comment field the user deliberately
left blank is a real answer, and if (comment) throws it away as if the field
were never there.
if (value) asks: is this value truthy? It does not ask whether the value
exists, whether it was set, or whether it is meaningful. When zero and empty
string are legitimate data — counts, scores, prices, totals, quantities in
stock — those are three different questions and you must pick the right one.
let goalsRecorded = 0;
if (goalsRecorded !== undefined) {
console.log("Bilal scored " + goalsRecorded + " goals.");
} else {
console.log("No score recorded for Bilal.");
}
Bilal scored 0 goals.
!== undefined asks the question you meant: has this been set at all? If the
missing value might be null as well, goalsRecorded != null covers both,
which is the one loose-equality idiom worth keeping.
The bare truthiness test is still fine when falsy really does mean absent — a name, a search term, an address. Use it knowingly, not by habit.
The ternary operator
When both branches do the same kind of thing and differ only in a value, a
full if block is four lines of ceremony for one decision. The ternary
operator compresses it.
let finalMarks = 44;
let result = finalMarks >= 50 ? "Pass" : "Fail";
console.log(result);
Fail
Read it as a question and two answers: condition, then ?, then the value if
true, then :, then the value if false. Unlike if, it produces a value, so
it can go straight into a variable or inside a template literal from
the strings lesson.
let cupsOrdered = 1;
console.log(`You ordered ${cupsOrdered} ${cupsOrdered === 1 ? "cup" : "cups"}.`);
You ordered 1 cup.
That is the ternary at its best — one small choice inside a sentence, where an
if block would force you to build the string in pieces.
It is at its worst when nested. A ternary inside a ternary inside a ternary is
a grading chain written to be difficult. If your decision has more than two
outcomes, use else if and let it take the space.
Conditions inside conditions
Branches can contain branches, which lets you narrow a decision in stages.
let hour = 15;
if (hour >= 8) {
if (hour < 17) {
console.log("The canteen is open.");
} else {
console.log("The canteen has closed for the day.");
}
} else {
console.log("The canteen has not opened yet.");
}
The canteen is open.
Two levels is usually readable. Three is a signal to stop and rethink — either combine the conditions with the logical operators, or handle the simple cases first and return early. Deep nesting is where logic goes to hide.
Handle the impossible cases first
The habit that pays off longest: at the top of any decision, deal with the values that should not be there before you deal with the ones that should.
let enteredScore = Number("not a number");
if (Number.isNaN(enteredScore)) {
console.log("That is not a valid score.");
} else if (enteredScore >= 50) {
console.log("Pass");
} else {
console.log("Fail");
}
That is not a valid score.
Without that first branch, NaN >= 50 is false, the chain falls through to
else, and a typing mistake gets reported as a failed exam. Every comparison
involving NaN is false, as
the numbers lesson explains, so bad input does
not announce itself — it quietly takes the last branch. Check for it before
you trust the number, and the rest of the chain only ever sees real values.
Prefer learning with a teacher?
WasiLearn Academy runs small-group classes covering this material, online and in Karachi.
See the classes