Conditional branching: if, '?'

Programs rarely march in a straight line. You check something, and depending on the answer you go one way or another: show an error if the form is empty, greet a user by name if they’re logged in, charge shipping only if the cart isn’t empty. That fork in the road is what conditional branching gives you.

JavaScript has two tools for it. The if statement runs a block of code when a condition holds. The conditional operator ? (spoken aloud as the “question mark” operator) picks between two values based on a condition. They overlap, but they’re built for different jobs, and knowing which to reach for is half the skill.

condition?truefalserun this branchrun that branchcontinue
A condition splits the flow of execution into branches.

The “if” statement

The if(...) statement takes a condition in parentheses, evaluates it, and — if the result comes out true — runs the block of code that follows.

Here’s the classic shape:

let year = prompt('In which year did humans first land on the Moon?', '');

if (year == 1969) alert( 'You are right!' );

The condition here is a plain equality check, year == 1969. It doesn’t have to be that simple. Any expression that can be judged true or false works — a comparison, a function call, a variable, a chain of && and ||.

When you want more than one statement to run under the condition, wrap them in curly braces:

if (year == 1969) {
  alert( "That's correct!" );
  alert( "You're so smart!" );
}

Without braces, only the single statement immediately after if (...) belongs to the condition. Both alerts above are tied together because the braces group them into one block.

Boolean conversion

Before deciding which branch to take, if converts whatever’s in its parentheses to a boolean. That conversion follows fixed rules, and it pays to know them cold — a surprising number of bugs trace back to a value being “falsy” when you assumed it was true.

These values all convert to false. They’re called falsy:

  • the number 0
  • the empty string ""
  • null
  • undefined
  • NaN

Everything else converts to true and is called truthy. That includes some values people expect to be false: the string "0", the string "false", an empty array [], and an empty object {} are all truthy.

falsy → false
false
0, -0, 0n
“”
null
undefined
NaN
truthy → true
everything else, incl.
1, -1, 3.14
“0”, “false”
“ “ (a space)
[], {}
any function
The eight things that are falsy — and the everything-else that isn't.

If you want the full picture of how these conversions work, the chapter on Type Conversions covers it in detail.

Put this in practice. The block below never runs, because 0 is falsy:

if (0) { // 0 is falsy
  ...
}

And this one always runs, because 1 is truthy:

if (1) { // 1 is truthy
  ...
}

You can also compute the boolean ahead of time and hand the finished value to if. This is handy when the condition is long or reused:

let cond = (year == 1969); // the equality evaluates to true or false

if (cond) {
  ...
}

The “else” clause

An if can carry an optional else block. It runs exactly when the condition turns out falsy — the “otherwise” branch.

let year = prompt('In which year did humans first land on the Moon?', '');

if (year == 1969) {
  alert( 'You guessed it right!' );
} else {
  alert( 'How can you be so wrong?' ); // any value other than 1969
}

One of the two blocks always runs, never both. There’s no way for execution to skip past both branches.

Several conditions: “else if”

When there are more than two outcomes, chain conditions with else if. Each one is tested only if all the earlier ones failed.

let year = prompt('In which year did humans first land on the Moon?', '');

if (year < 1969) {
  alert( 'Too early...' );
} else if (year > 1969) {
  alert( 'Too late' );
} else {
  alert( 'Exactly!' );
}

JavaScript walks the chain top to bottom. It checks year < 1969 first; if that’s falsy it moves on to year > 1969; if that’s also falsy it falls through to the final else. The moment a condition matches, its block runs and the rest of the chain is skipped entirely.

year < 1969 ?‘Too early…’truefalseyear > 1969 ?‘Too late’truefalseelse‘Exactly!’
An else-if chain is checked top to bottom; the first match wins and stops the search.

You can string together as many else if blocks as you need. The trailing else is optional — leave it off if there’s no sensible default action.

Conditional operator ‘?’

A common little chore: set a variable to one thing or another depending on a condition. Written with if, it’s five lines for what is really one decision:

let passed;
let score = prompt('What score did you get?', '');

if (score >= 60) {
  passed = true;
} else {
  passed = false;
}

alert(passed);

The conditional operator — also called the “question mark” operator — collapses that into a single expression.

It’s often called the ternary operator because it takes three operands, which is unusual: it’s the only operator in JavaScript that does. Here’s the shape:

let result = condition ? value1 : value2;

The condition is evaluated first. If it’s truthy, the whole expression becomes value1; if it’s falsy, it becomes value2. That result is what gets assigned.

let result =condition?value1:value2
condition truthy  →  result is value1
condition falsy   →  result is value2
The three operands of the ternary operator and how the result is chosen.

The five-line example above becomes one line:

let passed = (score >= 60) ? true : false;

The parentheses around score >= 60 are technically optional. The question mark operator has very low precedence, so the comparison >= runs first regardless:

// the comparison "score >= 60" runs first anyway,
// so no parentheses are strictly required
let passed = score >= 60 ? true : false;

Still, wrapping the condition in parentheses makes the three parts easier to see at a glance, so it’s a good habit.

Multiple ‘?’

Chain several ? operators together and you can pick from more than two values, one condition after another:

let temp = prompt('temperature?', 20);

let message = (temp < 0) ? 'Freezing!' :
  (temp < 15) ? 'Chilly.' :
  (temp < 30) ? 'Pleasant.' :
  'Scorching!';

alert( message );

It reads as a puzzle at first, but it’s just a top-to-bottom sequence of tests, the same idea as an else if chain:

  1. Check temp < 0. If true, the value is 'Freezing!' and we’re done.
  2. Otherwise, move past the colon and check temp < 15. If true, the value is 'Chilly.'.
  3. Otherwise, check temp < 30. If true, the value is 'Pleasant.'.
  4. Otherwise, fall through to the final value, 'Scorching!'.

Written with if..else, the same logic looks like this:

if (temp < 0) {
  message = 'Freezing!';
} else if (temp < 15) {
  message = 'Chilly.';
} else if (temp < 30) {
  message = 'Pleasant.';
} else {
  message = 'Scorching!';
}

Non-traditional use of ‘?’

You’ll sometimes see the question mark operator used to run code rather than to produce a value, standing in for a plain if:

let planet = prompt('Which planet is closest to the Sun?', '');

(planet == 'Mercury') ?
   alert('Right!') : alert('Wrong.');

Depending on planet == 'Mercury', either the first or the second expression after the ? gets evaluated — and since each is an alert(...) call, one of the two alerts fires. No result is stored anywhere; the operator is being used purely for its branching side effect.

Here’s the same behavior written with if:

let planet = prompt('Which planet is closest to the Sun?', '');

if (planet == 'Mercury') {
  alert('Right!');
} else {
  alert('Wrong.');
}

We read code by scanning down the page. A stack of clearly separated blocks is easier to follow than one long horizontal line, and it’s easier to extend when a branch grows to more than one statement.

The rule of thumb: use ? when you want to produce one value or another, and use if when you want to run one block of code or another. Match the tool to the intent and your code stays readable.