---
title: 'Conditions and Selection'
source: 'https://academia.sh/en/courses/javascript-fundamentals/conditions-and-selection'
course: 'JavaScript Fundamentals'
language: en
updated: '2026-08-23T07:00:57+00:00'
license: 'CC BY-SA 4.0'
---

# Conditions and Selection

`if` chains and guard clauses, the ternary operator, the `switch` statement's strict equality, fall-through behavior, and scope in case blocks.

The value model is complete. This topic covers building program flow with those values:
which code runs under which condition, how repetition is written, and how work is
split into functions. The Programming Fundamentals course's Conditional Branching lesson
established the purpose of these structures; here, JavaScript's own particular rules are
covered.

Three things about conditional selection are specific to the language: how a condition
is converted to a boolean value (previous lesson), the ternary operator being an
expression, and which equality `switch` uses.

## Condition Chains and Guard Clauses

The `if` statement converts the expression in parentheses to a boolean value and runs
the body if it is truthy. The chain continues with `else if`.

**Guard clauses** — conditions placed at the start of a chain that return early on an
invalid case — rescue the real logic from nested blocks:

```js
function classify(temperature) {
  if (temperature == null || Number.isNaN(temperature)) {
    return "invalid";
  }
  if (temperature < 18) {
    return "low";
  } else if (temperature < 22) {
    return "normal";
  } else {
    return "high";
  }
}

for (const value of [17.9, 18, 21.5, 22, 23, null, NaN]) {
  console.log(String(value).padEnd(5), classify(value));
}
```

```
17.9  low
18    normal
21.5  normal
22    high
23    high
null  invalid
NaN   invalid
```

The guard clause combines two checks. The `== null` spelling is the exception defined
in the Equality Comparisons lesson: it covers both the `null` and `undefined` cases.
`Number.isNaN` catches an invalid number; because `NaN` passes no comparison, without
this guard `classify(NaN)` would silently give `"high"` — all three comparisons would
come out false and flow would fall to the final `else` branch.

Which side a boundary value falls on is read from the output: `18` went to normal, `22`
went to high. The choice of comparison operator (`<` versus `<=`) sets these boundaries
and is part of the definition.

Curly braces are not required for single-statement bodies, but this course always
writes them. The reason is that a second statement added later would otherwise
silently fall outside the condition.

## The Ternary Operator

`if` is a statement: it produces no value, it directs flow. The ternary operator is an
**expression**: it produces a value and can be written anywhere a value is expected.

```js
const temperature = 21.5;
const status = temperature >= 22 ? "high" : temperature >= 18 ? "normal" : "low";
console.log(status);
console.log(`${temperature} degrees: ${temperature > 20 ? "above threshold" : "below threshold"}`);
```

```
normal
21.5 degrees: above threshold
```

The second line shows the concrete benefit of being an expression: a condition was
placed directly inside a template literal. An `if` statement could not have been written
there.

A chained ternary associates to the right; the second question mark sits inside the
first expression's false branch. This spelling quickly loses readability past two
branches. This course's rule: **a ternary is chained at most once**; anything more is
written as an `if` chain or a mapping object.

## The `switch` Statement

`switch` compares a value against multiple constants. Matching is done with **strict
equality** — one of the three operators from the Equality Comparisons lesson, `===`.

```js
function unit(field) {
  switch (field) {
    case "temperature":
      return "C";
    case "humidity":
      return "%";
    case "pressure":
      return "hPa";
    default:
      return "unknown";
  }
}
console.log(unit("temperature"), unit("humidity"), unit("wind"));
```

```
C % unknown
```

Using strict equality removes any expectation of coercion:

```js
function check(value) {
  switch (value) {
    case 1:
      return "number one";
    case "1":
      return "text one";
    default:
      return "no match";
  }
}
console.log(check(1), "|", check("1"), "|", check(true));
```

```
number one | text one | no match
```

The number `1` and the text `"1"` went to separate branches. The value `true` matched
neither — under loose equality `true == 1` would be true, but `switch` does not use
loose equality.

## Fall-Through

The `switch` statement starts at the matching `case` label and **flows downward until
it meets a `break` or `return`**. This behavior is called fall-through, and when done
unintentionally it produces a silent bug:

```js
function label(field) {
  let result = "";
  switch (field) {
    case "temperature":
      result = "temperature (C)";
    case "humidity":
      result += " humidity (%)";
      break;
    default:
      result = "unknown";
  }
  return result;
}
console.log(JSON.stringify(label("temperature")));
console.log(JSON.stringify(label("humidity")));
console.log(JSON.stringify(label("pressure")));
```

```
"temperature (C) humidity (%)"
" humidity (%)"
"unknown"
```

On the first line, flow fell from the first branch into the second and the two texts
concatenated. This was not the writer's intent. The outputs were written with
`JSON.stringify`; otherwise the leading space on the second line would not be visible.

Used deliberately, fall-through binds multiple values to the same branch:

```js
function group(station) {
  switch (station) {
    case "A1":
    case "A2":
      return "group A";
    case "B1":
      return "group B";
    default:
      return "unknown";
  }
}
console.log(group("A1"), "|", group("A2"), "|", group("B1"), "|", group("C9"));
```

```
group A | group A | group B | unknown
```

Empty `case` labels have no body; flow falls straight through to the next one. This is
an accepted use of the language and is not hard to read — falling from a branch **with**
a body into another will not be written in this course.

## Scope in Case Blocks

The entire `switch` body is **a single block**. Separate `case` labels do not open
separate scopes; this is why declaring the same name in two branches gives a parsing
error:

```js
switch (1) {
  case 1:
    const x = 1;
    break;
  case 2:
    const x = 2;
    break;
}
```

```
SyntaxError: Identifier 'x' has already been declared
```

The redeclaration rule from the Variable Declarations lesson shows up here in an
unexpected place. The fix is to wrap each branch in its own curly braces:

```js
switch (1) {
  case 1: {
    const x = "first";
    console.log(x);
    break;
  }
  case 2: {
    const x = "second";
    console.log(x);
    break;
  }
}
```

```
first
```

## Choosing a Selection Structure

The choice among the three structures is made according to the shape of the
comparison.

`switch` fits when a single value is compared against constants. Range checks, compound
conditions, or a comparison other than equality call for an `if` chain.

Constant mappings offer a third option: they can be written as key–value pairs in an
object and read directly.

```js
const UNITS = { temperature: "C", humidity: "%", pressure: "hPa" };

function unit(field) {
  return UNITS[field] ?? "unknown";
}
console.log(unit("temperature"), unit("wind"));
```

```
C unknown
```

This spelling keeps its readability as the mapping grows and separates data from code.
Using `??` is deliberate: if a key in the mapping had a value of `0` or an empty
string, `||` would have dropped that to the default too.

## Summary

- Guard clauses return early on an invalid case and rescue the real logic from nested
  blocks; without a `NaN` guard, flow silently falls to the final branch.
- The ternary operator is an expression and can be written anywhere a value is
  expected; it is chained at most once.
- `switch` matches with strict equality; it performs no coercion.
- Flow starts at the matching label and falls through until it meets `break` or
  `return`; bodyless labels bind multiple values to the same branch.
- The `switch` body is a single block; to declare a name in a branch, wrap each branch
  in its own curly braces.
- Constant mappings written as objects substitute for `switch` and take a default with
  `??`.

## Next Step

Conditions classified a single record. Processing the entire measurement list requires
repetition. The next lesson covers loop forms: the counted loop, the conditional loop,
and two different collection loops. The difference between the two collection loops —
one gives keys, the other gives values — produces unexpected results on arrays.
