This context discusses five alternatives to 'if' statements for conditional branching in JavaScript, including the ternary operator, switch statement, jump table, dynamic dispatch, and pattern matching.
Abstract
The provided context explores five alternatives to 'if' statements for conditional branching in JavaScript. The ternary operator is introduced as a concise way to write conditional statements. The switch statement is presented as a method to handle multiple conditions. The jump table is discussed as a way to use a lookup table to branch into a new function or block. Dynamic dispatch is explained as a way to select which polymorphic method to call based on an object's type. Lastly, pattern matching is introduced as a new approach to branching, popular with those who favor a more functional programming style. The article concludes by suggesting that avoiding branching altogether is often a good starting point, but if it's necessary, choosing the most appropriate mechanism is crucial.
Bullet points
The ternary operator is a concise way to write conditional statements.
The switch statement is used to handle multiple conditions.
The jump table uses a lookup table to branch into a new function or block.
Dynamic dispatch selects which polymorphic method to call based on an object's type.
Pattern matching is a new approach to branching, popular with those who favor a more functional programming style.
Avoiding branching altogether is often a good starting point, but if it's necessary, choosing the most appropriate mechanism is crucial.
5 Alternatives to ‘If’ Statements for Conditional Branching
Concepts for implementing conditional branching with examples in JavaScript
SVG by Booyabazookaoriginal; PNG by Wapcaplet / CC BY-SA
Conditionalbranching is when a segment of code is executed or evaluated based on a condition, typically implemented using an if...else construct.
For example:
if (customerPaid)
{
sendThankYou()
}
else
{
sendReminder();
}
Here, sendThankYou() will get called if customerPaid is true; otherwise sendReminder() will be called.
There’s some thinking in the programming community that if should be considered harmful or a code smell. Regardless of the validity of this claim, there may be cases where if isn’t the best approach to branching, or where branching should be avoided altogether.
In this tutorial, I’ll therefore demonstrate six alternatives to if...else with some discussion on when you might choose to use them.
Examples are written in JavaScript, but the concepts are applicable in many other languages.
1. The Ternary Operator
One of my favourite alternatives to if...else is the ternary operator.
This takes the form:
condition ? expressionIfTrue : expressionIfFalse
Here expressionIfTrue will be evaluated if condition evaluates totrue; otherwise expressionIfFalse will be evaluated.
The beauty of ternary operators is they can be used on the right-hand side of an assignment.
This will set labelText to “Thank You!” if the customer has paid; otherwise, you’ll get “Payment Overdue”.
2. The Switch Statement
A switch statement has the following structure:
The break statements are optional and will cause the switch block to be exited. If a break is omitted, then execution will proceed to the next case statement.
switch is useful for branching based on a homogeneous set of values.
For example:
3. The Jump Table
A jump table is a method of conditionally branching using a lookup table to branch into a new function or block.
Technically in C, this would be implemented as an array of function pointers. However, unless we strictly need the efficiency of array lookup, we could generalise this to any kind of keyed function lookup.
For example, the above switch statement could be refactored as a jump table in JavaScript, like so:
Here stop and go are function objects. Let’s flesh this out a bit to make it clear how this approach would work in context:
Here, our code branches conditionally based on the value of the colour variable. The semantics are the same as our switch example, but we have less code. And it’s (arguably) clearer to read.
4. The Dynamic Dispatch
Another alternative to using if statements is a dynamic dispatch. This involves selecting which polymorphic method to call based on an object’s type.
It could be used to branch conditionally, like this:
Here, a different code path is taken depending on the type of object passed to the handleShape function. In our case, Shape is a Square, so the area is logged as 4.
This approach can result in a lot more code, but there are some arguments for its usage over an if...else. This kind of pattern is generally appropriate when the code already employs OOP; however, seeking to always engineer branching to use polymorphism seems like overkill.
5. ‘try' and 'catch' statements
I’m including this for completeness since try/catch should rarely, if ever, be used for control flow.
Much has been written about why this is a bad idea, but my personal take is that try/catch are designed for handling exceptions, so by using them for control flow, our intent becomes unclear.
In any case, here’s an example of conditional branching using exceptions:
Here, item will get assigned topreviousItem if it exists; otherwise, a ReferenceError exception will be thrown, and a new Item object will be assigned.
6. Pattern Matching
Pattern matching is the new kid on the block when it comes to branching. It’s generally popular with those who favour a more functional programming style.
Pattern matching could be thought of as dynamic dispatch (see above) based on a value rather than a type.
A classic example is implementing a factorial function using two functions:
factorial(0) ::= 1factorial(n) ::= n * factorial(n-1)
The first is called if the value passed in is zero, in which case 1 is returned. The second is called if the value passed in is nonzero — in which case, the result of the expression n * factorial(n-1) is returned.
Some languages have pattern matching built in. In JavaScript, an external library needs to be used — for example, Z (pattern matching for Javascript).
The factorial function could be written using Z as follows:
There are arguably more readable and expressive ways to compute a factorial in JavaScript, but the potential for pattern matching as a means of conditional execution extends well beyond this simple example.
There is a proposal to build a pattern into the JavaScript language, which is worth a read.
Conclusion
Conditional branching can introduce complexity into a program.
Nested if and switch statements can make code less readable and open potential for bugs. Other forms of branching can result in an overengineered codebase. If it’s possible to avoid branching altogether, this is often a good starting point.
However, if we do need to conditionally branch, it’s good to choose the most appropriate mechanism for the job. Hopefully, in the article I’ve provided some useful options for consideration.