JAVASCRIPT / CONDITIONALS
Common branching mistakes and how to avoid them
Spot and fix conditions that only look correct: always-true comparisons, falsy zero checks, unreachable branch order, and braceless if bodies.
What you will learn
- Print a suspicious condition with console.log before trusting it inside an if
- Expand day === 'Sat' || 'Sun' into one full comparison per accepted value
- Test count === undefined instead of !count when 0 or '' are valid data
- Order overlapping range checks narrowest first so no branch becomes dead code
Understanding Common branching mistakes and how to avoid them
The parser does not read English. An if takes exactly one expression, evaluates it to a single value, then coerces that value the same way Boolean() would. Almost every value is legal inside the parentheses, so when the expression you typed asks a different question than the one in your head, nothing complains at all. `day === 'Sat' || 'Sun'` is parsed as `(day === 'Sat') || 'Sun'`, and when the first half is false the whole condition becomes the string 'Sun', which is truthy for every day of the week.
A second family of bugs involves conditions that are each correct but arranged so some can never win. A chain of tests stops at the first one that passes, so a broad test placed ahead of a narrow one turns the narrow branch into dead code, and JavaScript cannot warn you because both tests are perfectly valid. The habit that catches this is picking one sample value per branch and tracing which test claims it first.
The third family is confusing "falsy" with "missing". `!value` is true for undefined, null, 0, NaN, '' and false, so using it to mean "the caller gave me nothing" quietly throws away legitimate zeros and empty strings, and the bug only surfaces the day a real quantity is 0. Ask the precise question instead: `value === undefined` for an absent argument, `value == null` for absent-or-null, `Number.isNaN(value)` for a failed parse. The same discipline applies to shape: keep braces on every body, and always use === in a test so a slipped single = cannot both overwrite a variable and report truthy.
function isWeekend(day) {
// Parsed as (day === 'Sat') || 'Sun'
return day === 'Sat' || 'Sun';
}
function isWeekendFixed(day) {
return day === 'Sat' || day === 'Sun';
}
console.log(isWeekend('Mon'));
console.log(Boolean(isWeekend('Mon')));
console.log(isWeekendFixed('Mon'));
console.log(isWeekendFixed('Sat'));
function describe(count) {
if (!count) return 'no data'; // 0 is falsy, but it is real data
return count + ' items';
}
function describeFixed(count) {
if (count === undefined) return 'no data';
return count + ' items';
}
console.log(describe(0));
console.log(describeFixed(0));A condition is an expression that JavaScript reduces to one value and then coerces to a boolean, so a condition that reads correctly in English can still be a value that is always truthy.
Worked examples
A branch that can never run
Shows how a wide range test placed first makes the narrower branch below it unreachable.
function grade(score) {
if (score >= 50) return 'pass';
if (score >= 90) return 'distinction';
return 'fail';
}
function gradeFixed(score) {
if (score >= 90) return 'distinction';
if (score >= 50) return 'pass';
return 'fail';
}
console.log(grade(95));
console.log(gradeFixed(95));
console.log(gradeFixed(60));
console.log(gradeFixed(20));Example explained
Line 1score >= 50 is already true for 95, so grade returns 'pass' and the 90 test is never evaluated.
Line 2Every score of 90 or more also satisfies 50 or more, so the second line of grade is dead code.
Line 3gradeFixed only reorders the tests; no extra condition such as score < 90 is needed.
Line 4Checking one value per range (95, 60, 20) proves each branch is reachable.
= instead of === inside the test
Demonstrates that an assignment in a condition both changes the variable and reports the assigned value.
let status = 'idle';
if (status = 'error') {
console.log('handling error, status is', status);
}
status = 'idle';
if (status === 'error') {
console.log('this never prints');
} else {
console.log('still', status);
}Example explained
Line 1status = 'error' is an assignment expression whose value is the string 'error', and every non-empty string is truthy.
Line 2So the block runs even though status was 'idle' a moment earlier, and the variable is now corrupted too.
Line 3The second test with === compares without writing, so the else branch runs and status keeps its value.
Line 4Writing the constant on the left, as in 'error' = status, turns the same slip into an immediate SyntaxError.
Indentation is not the body
Shows what a braceless if actually controls, and what a stray semicolon after the condition does.
let n = -5;
let label = '';
if (n > 0)
label = 'positive';
console.log('checked positive branch');
console.log('label is', JSON.stringify(label));
if (n > 0);
{
console.log('this block always runs');
}Example explained
Line 1A braceless if governs exactly one statement, so only label = 'positive' is conditional.
Line 2The console.log below it is a separate statement that runs for every value of n, even though it is indented.
Line 3label is still the empty string, which JSON.stringify shows as "" so you can see it at all.
Line 4The semicolon after if (n > 0) is an empty statement acting as the whole body, leaving the following block as ordinary unconditional code.
Important notes
if (1 < x < 10) is legal JavaScript, not a syntax error: 1 < x produces a boolean that is coerced to 0 or 1 before the second comparison, so with x = 42 the whole condition is still true. Write 1 < x && x < 10.
Never test computed decimals with ===; 0.1 + 0.2 === 0.3 is false because of binary floating point, so compare with a tolerance such as Math.abs(a - b) < 1e-9.
Common mistakes
Writing if (day === 'Sat' || 'Sun') instead of two comparisons: the second operand is a truthy constant, so the branch fires for Monday and everything else.
Using if (!count) to mean "no value was supplied": a real count of 0 or an empty string takes the missing-value path and valid data is silently dropped.
Listing score >= 50 before score >= 90: the distinction branch becomes unreachable and top students are reported as a plain pass, with no error to warn you.
Try it yourself
Change, predict, then run
In a browser console define function ship(state) { if (state === 'paid' || 'pending') return 'ship'; return 'hold'; } and call ship('cancelled'). Print the bare condition value for that input to see why it ships, then rewrite the condition so only 'paid' and 'pending' return 'ship'.
Open the JavaScript workspaceCheck your understanding
A function contains if (qty !== 0 || qty !== null) { charge(qty); } and charge runs for every value of qty. What explains it?
- A single value cannot be equal to both 0 and null, so at least one !== test is always true and || accepts it
- !== cannot compare against null, so the second test is always true
- || converts both operands to numbers before comparing them
- The condition is invalid and JavaScript treats invalid conditions as true
Show answer
For any qty at most one of the two tests can fail, so || always finds a true operand; joining the tests with && instead gives the intended "neither 0 nor null". Option 2 is tempting because null behaves oddly with ==, but !== handles null fine: null !== 0 is true precisely because they are different values. The faulty part is the operator joining the tests, not the null comparison.