Course: JavaScript

Progress (0%)

  1. Foundation

  2. Numbers

  3. Strings

  4. Conditions

  5. Loops

  6. Arrays

  7. Functions

  8. Objects

  9. Exceptions

  10. HTML DOM

  11. CSSOM

  12. Events

  13. Drag and Drop

  14. opt Touch Events

  15. Misc

  16. Project: Analog Clock

JavaScript switch Statement

Chapter 23 28 mins

Learning outcomes:

  1. The idea behind switch
  2. What is switch and its syntax
  3. What is fallthrough
  4. The default keyword
  5. When not to use switch

The idea behind switch

Sometimes while programming, you'll come across a case where you need to test the same thing for different values of the same type over and over again, and perform different actions on each outcome.

Following is an example of such a case. The program simply converts a star rating from 1 to 5 into a description of that rating:

var rating = 1;

if (rating === 1) {
   console.log('Very bad');
}
else if (rating === 2) {
   console.log('Bad');
}
else if (rating === 3) {
   console.log('Average');
}
else if (rating === 4) {
   console.log('Good');
}
else if (rating === 5) {
   console.log('Excellent');
}

There is a variable rating that holds a number, and then a whole list of if and else if statements to make different logs based on the value of rating.

Notice the type of each value that we compare with rating — every single value is a number, and so we are only checking for different values within the same kind of data type in each if.

Obviously when we run the code above, we get the following log since rating is 1:

Very bad

The code works absolutely fine, however there is a substantial amount of repetition in it.

See how we have to write rating === again and again in all the statements in addition to having to write else if a couple of times as well.

Fortunately, there is a much better construct in JavaScript, exclusively meant to handle such a case pretty elegantly — the switch statement.

What is switch?

The switch statement is yet another selection statement in JavaScript in addition to the if and else statements.

Defining it precisely:

The switch statement is a conditional statement that literally switches over given blocks of code depending on whether they meet a given criterion.

The basic syntax of a switch statement is quite different from that of an if statement. However, it's still simple to understand.

Here's the minimal syntax of switch:

switch (expression) {
   case match1:
      statements;
   case match2:
      statements;
   /* ... */
   case matchN:
      statements;
}

We start off with the switch keyword, followed by a pair of parentheses (()) containing the expression expression that we ought to test in the statement.

Next comes a pair of curly braces ({}) denoting the body of the switch statement. This is formally referred to as the case block.

This pair of curly braces, unlike the case with if, else, for and while, does NOT denote a block statement. It's purely a part of the syntax of switch.

The case block is comprised of one or possibly more case statements. Each of these case statements is formally referred to as a case clause.

A case clause simply denotes one possible case that expression could be equal to. Clearly, since a given expression could be equal to one of many values, we could include many case clauses inside a switch statement.

A case clause begins with the case keyword, followed by the expression (match1, match2, ..., matchN) to match the main expression against. Note that this matching is done using the same algorithm that's used by the identity operator (===) in JavaScript.

Then comes a colon (:) followed by a set of statements corresponding to the case clause, customarily indented and written on a new line after the colon.

In the snippet above, we've shows this set of statements as a single statements placeholder, but keep in mind that it actually means that we can have a whole bunch of statements there (without having to use a block statement ({}).

Where a new case keyword appears, the previous case clause (if there is any) ends.

And that's pretty much it.

Let's now consider an example to help us understand this syntax even better.

A simple example

Remember the star rating program we created above using a bunch of if and else if commands? It's now time to rewrite it using switch.

But before we do that, we have to clarify one thing regarding the switch statement, which we explore in even more detail in the next section.

Breaking out of a case clause

By default, when case clauses are matched against the main expression (specified next to the switch keyword), if a case matches, its corresponding set of statements is executed, of course, but then the next case clause is also executed, and then the next one as well, and then the next one, and so on.

You might think that this is counter-intuitive, but it's not, which we'll explain how later on in the section below.

For now, you should know that to override this default behavior, we can use the break keyword right at the end of each clause's set of statements. It literally helps us 'break out' of the current case clause and, in fact, the entire switch statement.

Back to the star rating program.

First, let's decide what we need to test. What do you think? Well, we need to test the rating variable. One step done.

Next, let's see if we can enumerate the possible values of rating. Well, we surely could. It can be either 1, 2, 3, 4 or 5. And we're done.

We've successfully determined the two main things required in a switch statement, i.e. the value to test and the possible cases for it to be equal to, and so now we can finally get to the actual coding.

Here's the star rating program rewritten using switch:

var rating = 1;

switch (rating) {
   case 1:
      console.log('Very bad');
      break;
   case 2:
      console.log('Bad');
      break;
   case 3:
      console.log('Average');
      break;
   case 4:
      console.log('Good');
      break;
   case 5:
      console.log('Excellent');
      break;
}

Let's understand what's going on here:

  • As determined before, we ought to test the rating variable. Hence, it goes inside the pair of parentheses (()) next to the switch keyword.
  • Inside the case block, we lay out all the cases that rating could possibly be equal to. Once again, as determined before, there were a total of five cases, and that's exactly the total number of our case clauses.
  • For each of these cases, we have a corresponding set of statements to execute if rating matches that case.
  • Each set of statements ends with break in order to exit out of the switch statement and likewise prevent execution from entering into the next case when a given case matches.

Now, let's try running the code:

Very bad

Exactly the same output as before. Wonderful!

Solving scenarios similar to the one in this example is the de facto application of switch.

That is, when we want to compare a variable against numerous values, all of the same type, and then execute a piece of code for each of them, switch is the go-to construct to employ.

What are fallthroughs?

In the previous section, we saw the break command being used inside every single case clause in order to prevent execution from tipping over into the next clause once it enters into any one. Now, we shall understand more about this behavior.

By default, when JavaScript is done executing a case clause (when it matches the main expression to test), it continues to the next clause, and then to the next one, and so on, until it's explicitly instructed to exit out or it reaches the very end of the switch statement.

This behavior of continuing execution to the next case clause is commonly known as fallthrough. Executions literally 'fall through' to the next case clause, and hence the name.

At first glance, fallthrough might seem totally counter-intuitive. However, it ain't. In fact, it's a pretty desirable thing.

But how? Well, that can best be understood with the help of an example.

Suppose we have a variable num and want to see its remainder when divided by 4. If the remainder is 0 or 1, we must log 'First two', or otherwise if it's 2 or 3, we must log 'Last two'.

Now going with our old approach to laying out switch, we produce the following code:

var num = 5;

switch (num % 4) {
   case 0:
      console.log('First two');
      break;
   case 1:
      console.log('First two');
      break;
   case 2:
      console.log('Last two');
      break;
   case 3:
      console.log('Last two');
      break;
}

Each case clause is clearly showcased and the code indeed does what it's asked to do.

First two

Yet, the code can't be classified as one of the best code snippets out there. There clearly is a little bit of undesirable repetition in it.

Technically, the outcome of both case 0 and case 1 is the exact same and so we shouldn't ideally be repeating the exact same console.log() statement in both of them. The same goes for the last two cases.

At least for now, since we only need to log the value, the repetition might not seem like an issue. But imagine if the code to be repeated consisted of a hundred lines.

Would it then be sensible to repeat the whole code again? Clearly not!

To address cases like these, where a single set of statements applies to multiple case clauses, the switch statement was designed to take on the behavior of fallthrough.

A case clause can be without a corresponding set of statements, and in that case, if it matches the main expression of the switch statement, execution falls through to the next case clause.

Fallthrough is more or less analogous to the logical OR (||) operator used in an if's condition.

For example, if we rewrite the code above using if and ||, here's what we get:

var num = 5;

if (num % 4 === 0 || num % 4 === 1) {
   console.log('First two');
}
else if (num % 4 === 2 || num % 4 === 3) {
   console.log('Last two');
}
First two

The if checks for the first two cases while else if checks for the rest two of them.

We could've just used else here instead of else if but we didn't in order to showcase the exact mapping of the last two case clauses to a corresponding else if.

Notice how there are just two log statements here, instead of four. This is good because we have prevented some repetition.

But now there is another kind of repetition which is in the expression that we ought to compare, i.e. num % 4. It could very easily be prevented by creating a separate variable to hold the result of num % 4 and then using the variable in the comparisons instead.

Obviously, however, we can do much better if we instead use switch.

So, let's now rewrite the code above using switch and its fallthrough mechanism:

var num = 5;

switch (num % 4) {
   case 0:
   case 1:
      console.log('First two');
      break;
   case 2:
   case 3:
      console.log('Last two');
      break;
}

See how we've stripped off the set of statements from the first case clause and the third one. Syntactically, this code says that "if the variable num has a remainder of 0 or 1, log 'First two', or otherwise if it has a remainder of 2 or 3, log 'Last two'."

As mentioned before, if the fallthrough behavior is not desired, break could be used. The break statement effectively cuts off the fallthrough mechanism and literally breaks out of a switch statement.

For instance, the code below won't work as expected:

var num = 4;

switch (num % 4) {
   case 0:
      // Because of the following break, the switch statement
      // won't execute beyond it!
break; case 1: console.log('First two'); break; case 2: case 3: console.log('Last two'); break; }

We've changed the value of num to 4 so that the main expression num % 4 to test for matches the first case (in line 4). Since this case clause has a break in it, execution immediately terminates as soon as it's encountered.

The result is that there is nothing logged in the console.

We always have to be careful with such small kinds of details because, as in the case above, they can lead to unexpected outcomes.

Moving on, keep in mind that being able to break out of a switch statement isn't just limited to break; we can do the exact same thing using return as well, but we ought to make sure that it's used only inside a function.

Remember that using return outside a function is illegal!

In the code below, we restructure our star rating program into a function getRatingDescription() which takes in a given rating and returns back its description:

function getRatingDescription(rating) {
   switch (rating) {
      case 1:
         return 'Very bad';
      case 2:
         return 'Bad';
      case 3:
         return 'Average';
      case 4:
         return 'Good';
      case 5:
         return 'Excellent';
   }
}

var rating = 1;
console.log(getRatingDescription(rating));
Very bad

Notice how we don't have to use break in each case clause. That's simply because return is doing the exact same thing.

Precisely, return not just breaks execution out of switch but also out of the entire enclosing function.

If we were to alter this code by removing the returns and storing the description in a local variable, as shown below, it would cease to work properly:

function getRatingDescription(rating) {
   var description;
   switch (rating) {
      case 1:
         description = 'Very bad';
      case 2:
         description = 'Bad';
      case 3:
         description = 'Average';
      case 4:
         description = 'Good';
      case 5:
         description = 'Excellent';
   }
   return description;
}

var rating = 1;
console.log(getRatingDescription(rating));
Excellent

rating is 1 yet the returned description string is 'Excellent'. Clearly, there's something wrong in the code. And it turns out, fallthrough is to blame:

When the first case matches, description does get set to the corresponding string 'Very bad' but then by virtue of fallthrough, the next case clause executes as well, and then the next one, and so on, all the way to the very last case clause, ultimately setting description to 'Excellent'.

So no matter which value (1, 2, 3, 4 or 5) comes into the function as rating, description gets assigned the value 'Excellent'. And this is absolutely wrong.

If we want to keep using this variable approach, then we ought to use break after every variable assignment statement in order to prevent fallthrough.

To boil it all down, whenever using switch make sure to evaluate every case clause and see if fallthrough is really desired, and if not, then whether a break or return is being used to prevent it.

The default clause

Just how we have else to execute a given piece of code when the preceding if doesn't match, so do we have default for switch.

As the name suggests, the default keyword is used to specify the default clause to execute in a switch statement when neither of the given cases matches.

We can also refer to the default clause as the fallback clause. That is, when nothing gets matched, we 'fall back' on to the default clause.

Syntactically, it could be expressed as follows:

switch (expression) {
   case match1:
      statements;
   case match2:
      statements;
   /* ... */
   case matchN:
      statements;
   default:
      statements;
}

Although, we've written default as the last clause above, and it makes sense to have the default clause at the very end, there's no need to. We could have default anywhere in between the clauses.

This is because regardless of where default is inside a switch statement, it is evaluated only after all the rest of the case clauses have been evaluated and when none of them yields a match.

However, we'll stick to writing the default clause in the end to make this notion, that the default clause is evaluated last, explicit

Let's consider an example.

Suppose we want to test a variable char to see if it is a vowel or a consonant. If it is a vowel (a, e, i, o, u), we need to log 'Vowel', or otherwise log 'Consonant'. Quite basic.

Here's how we'd implement this program:

var char = 'a';

switch (char) {
   case 'a':
   case 'e':
   case 'i':
   case 'o':
   case 'u':
      console.log('Vowel');
      break;
   default:
      console.log('Consonant');
}

The cases that could be easily enumerated, i.e. the five vowels, are listed as case clauses with their respective log statement, while the rest of the cases, i.e. the consonants, are all represented in one single default clause, again with their respective log statement.

Obviously, when we'd run the program, we'd get 'Vowel' logged, since char is 'a' — a vowel:

Vowel

Pretty simple, isn't this?

Now, it's time for you to predict the output of a similar piece of code.

What does the following code log?

var text = 'JavaScript';

switch (text[1]) {
   case 'a':
   case 'e':
   case 'i':
   case 'o':
   case 'u':
      console.log('Vowel');
   default:
      console.log('Consonant');
}
  • Vowel
  • Consonant
  • Vowel Consonant

Remember that the default clause is the last clause that is executed in a switch statement, even if it's mentioned at the start of a switch statement.

However, once execution enters into a default clause, the fallthrough mechanism works in it the same way as it otherwise would in a normal case clause.

When not to use switch?

At this point in this chapter, we know a couple of things about switch: what exactly is switch, how to write it, what is fallthrough, what's the break keyword, and so on and so forth.

We sure have come to know a lot about this new conditional construct of JavaScript, but still there is another equally important thing that we haven't specifically shed light on.

And that is: when to and when not to use switch.

We'll talk about the latter, i.e. when not to use switch, which would automatically tell us about when should we actually use it.

There are just two cases, with one being the default one

If there are only two cases to address, and one of them is the default case, then it would be an overkill to use a switch. Such a problem can easily and more compactly be solved using if and else.

For instance, suppose we want to make different logs depending on whether a given number is even or odd.

Doing this with switch would look something as follows:

var num = 20;

switch (num % 2) {
   case 0:
      console.log('Even');
      break;
   default:
      console.log('Odd');
}

Whereas doing the same thing with if and else would look like this:

var num = 20;

if (num % 2 === 0) {
   console.log('Even');
}
else {
   console.log('Odd');
}

Which one seems more readable and sensible? The second one, indeed.

Hence, when there are two cases to deal with and one of them is the fallback case, then we're much better off with sticking to the good old if and else.

The comparisons aren't identity comparisons.

As you already know, each case clause's expression is compared with the main expression of the switch statement using the same algorithm used internally by the identity (===) operator.

Unfortunately, if we want to lay out conditions using other operators, such as <= or >=, or instanceof, then we can NOT use switch at all!

For example, let's suppose we want to check if a given number is greater than 10.

With the if statement, we can accomplish this very easily:

var num = 30;

if (num > 10) {
   console.log('Greater than 10');
}

But how can we do the same thing with switch? Well, we just can't!

Remember that switch is only meant to check whether a given expression evaluates down to a specific value, NOT to check whether it falls within a given range, or whether it is related to some other value in some way, or anything else.