# 条件语句的if语句的5种替代方法

Conditional branching 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.三元运算符 (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.

For example:

const labelText = customerPaid ? "Thank You!" : "Payment Overdue";

This will set labelText to “Thank You!” if the customer has paid; otherwise, you’ll get “Payment Overdue”.

# 2. Switch语句 (2. The Switch Statement)

A switch statement has the following structure:

switch语句具有以下结构：

switch (expression)
{
case value1:
// code executed if expression matches value1
[break;]
case value2:
// code executed if expression matches value2
[break;]
...
case valueN:
// code executed if expression matches valueN
[break;]
[default:
// code executed if expression has no match
[break;]]
}

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.

break语句是可选的，将导致退出switch块。 如果省略break ，则执行将继续执行下一个case语句。

switch is useful for branching based on a homogeneous set of values.

switch对于基于同类值集进行分支很有用。

For example:

switch (colour)
{
case 'red':
case 'amber':
stop();
break;
case 'green':
case 'flashing amber':
go();
break;
default:
throw 'Invalid colour';
}

# 3.跳转表 (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:

const handleColour = {    'red'               : stop,    'amber'             : stop,    'green'             : go,    'flashing amber'    : go}

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:

stopgo是功能对象。 让我们充实一下，以明确此方法在上下文中的工作方式：

const stop = () => { console.log("stop called"); }
const go = () => { console.log("go called"); }

const colour = 'red';

const handleColour =
{
'red'               : stop,
'amber'             : stop,
'green'             : go,
'flashing amber'    : go
}

handleColour[colour]();   // logs "stop called"

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.动态调度 (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:

const handleShape = (shape) => { console.log(shape.area()); }

class Shape
{
constructor(width = 2)
{
this.width = width;
}
area()
{
return 0;
}
}

class Square extends Shape
{
area()
{
return this.width ** 2;
}
}

class Circle extends Shape
{
area()
{
return Math.PI * (this.width / 2) ** 2;
}
}

const shape = new Square();

handleShape(shape);    // logs 4

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.

# try' and 'catch' statements(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:

var item;

try
{
item = previousItem;
}
catch (ReferenceError)
{
item = new Item();
previousItem = item;
}

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.模式匹配 (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:

var factorial = number => {  return number.matches (    (x = 0) => 1,    (x)     => x * factorial (x - 1)  )}factorial(6) // returns 720

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.

• 1
点赞
• 0
评论
• 3
收藏
• 扫一扫，分享海报

01-22 51

07-15 246
03-18 2万+
03-20 3457
04-30 4万+
12-13 1518
03-21 234
01-09 181
01-24 8358