[JavaScript Weekly #570] rethink ternary operators

🥳 Welcome interested partners to do something meaningful together!

I launched a weekly translation program, Warehouse addressAccess address

There is still a shortage of like-minded partners. They are purely personal interests. Of course, they will also help to improve English and front-end skills. Requirements: English is not too bad, github is proficient, persistent, modest and responsible for what you do.

If you want to participate, you can wx You can also send issue messages to the warehouse. My blog also has specific personal contact information: daodaolee.cn

preface

We all want the code to be clear and concise, but sometimes we can only choose between clear and concise. Fewer lines of code means fewer hidden errors, but clear, readable code is easier to maintain and modify. In general, traditional thinking tells us that clarity is better than simplicity. If you must choose between readability and brevity, choose readability.

Therefore, it is not unreasonable for many people to be skeptical about ternary operators. Of course, it's more concise than if statement, but it's also easy to write ternary as shit mountain. So be careful when dealing with problems. I generally prefer if statements, although they have a little problem with readability.

Ternary complexity

So why do some developers doubt ternary operators? Let's take a closer look at some of them.

strange

One of the reasons people don't like ternary operators is that they are too weird. JavaScript has many binary operators - operators that act on two expressions. Let's mention arithmetic operators, such as +, -, * and /, as well as Boolean operators such as & &, |, and = = =. There are at least 28 binary operators in total (depending on the current ECMAScript version). They are intuitive to use: expressions on the left, operator symbols, and expressions on the right.

There are fewer unary operators, but they are not that weird. Let's mention the negative operator:!. You may also have used the unary form of + and - for example, - 1. In most cases, they operate on the expression to the right of the symbol and are more convenient to use.

Ternary operator, as its name implies, operates on three expressions. Therefore, we use two symbols to write it:? And:. Otherwise, we cannot distinguish the start and end positions of the intermediate expression. So it's written like this:

(/* First expression*/) ? (/* Second expression */) : (/* Third expression */)

In the real scene, it's like this:

const protocol = (request.secure) ? 'https' : 'http';

If the first expression is "true", the ternary resolves to the value of the second expression, otherwise it resolves to the value of the third expression.

But that's not the only weird thing about it. Most binary operators have the same type: arithmetic operators work with numbers, Boolean operators work with Boolean values, and bitwise operators work with numbers. For these, the types on both sides are the same. But ternary operators have strange types: with ternary operators, the second and third expressions can be of any type, but the interpreter always converts the first to a Boolean value. This is weird for developers.

Not friendly to beginners

Unlike if statements, it is difficult to interpret ternary statements as pseudo syntax. For example, suppose we have an IF statement like this:

if (someCondition) {
    takeAction();
} else {
    someOtherAction();
}

If someCondition is true, the function takeAction is called; otherwise, the function someOtherAction is called. In comparison, the characteristics of ternary are not very obvious. Although ternary operators are composed of mysterious symbols, they don't read like normal English grammar. For beginners, you may have a headache.

Not suitable for reading

Even if you are not a beginner, triples are difficult to read. In particular, the ternary bracket length expression:

const ten = Ratio.fromPair(10, 1);
const maxYVal = Ratio.fromNumber(Math.max(...yValues));
const minYVal = Ratio.fromNumber(Math.min(...yValues));
const yAxisRange = (!maxYVal.minus(minYVal).isZero()) ? ten.pow(maxYVal.minus(minYVal).floorLog10()) : ten.pow(maxYVal.plus(maxYVal.isZero() ? Ratio.one : maxYVal).floorLog10());

Is it difficult to understand what happened. Each expression in a triplet has at least two linked method calls. Not to mention another triplet nested in the final expression. I don't recommend you to write such code!

Of course, we can make it a little better by adding spaces and line breaks:

const ten        = Ratio.fromPair(10, 1);
const maxYVal    = Ratio.fromNumber(Math.max(...yValues));
const minYVal    = Ratio.fromNumber(Math.min(...yValues));
const yAxisRange = !maxYVal.minus(minYVal).isZero()
                 ? ten.pow(maxYVal.minus(minYVal).floorLog10())
                 : ten.pow(maxYVal.plus(maxYVal.isZero() ? Ratio.one : maxYVal).floorLog10());

As Martin Fowler said: any fool can write code that the computer can understand, and only good programmers can write code that the human can understand.

if really good

Triples have their own disadvantages, but they have some advantages:

  1. The biggest reason for using ternary is simplicity
  2. The if statement also applies in the position of ternary

Of course, they are very different. Let's look at two pieces of code:

// if statement
let result;
if (someCondition) {
    result = calculationA();
} else {
    result = calculationB();
}

// three yuan
const result = (someCondition) ? calculationA() : calculationB();

In a way, the two pieces of code are equivalent. At the end of these two pieces of code, a result variable will be set to a value: the return value of calculationA() or calculationB(). But from another perspective, the two examples are completely different: if is a statement, and ternary is an expression. In other words, an expression always calculates a value. It is a separate code block, not a declaration.

This is an important concept. An expression evaluates to a value, and a declaration cannot assign the result of a statement to a variable or pass the result of a statement as a function parameter. if is a statement, not an expression.

To some extent, this is the core idea of functional programming. In order to avoid small problems in the code, we will use sentences to avoid some problems. If I can, I prefer to use pure functions. If a function is a pure function, it is OK to know that it does nothing but perform logical processing and return a value.

Let's look at this Code:

if (someCondition) {
    takeAction();
} else {
    someOtherAction();
}

takeAction and someOtherAction have no return value and will jump out of the current block. Will they cause some hidden dangers?

Let's look at the ternary operator

We like expressions because they can be combined better than statements. We can use operators and functions to build simple expressions into complex expressions. For example, we can use join operators to build complex strings:

('<h1>' + page.title + '</h1>');

We can pass this expression as a function parameter. Or we can use more operators to combine it with other expressions. Combining expressions is a great way to write code.

For example, if statements and for loops have nothing to do with each other, but they can be nested at will.

Expressions are more like Lego blocks. Their creation method is limited, and the small pieces at the top are connected with the gaps at the bottom of the brick. But once added, the brick will form a new shape. And the shape can be interchanged with any other shape with the same configuration. Consider the following figure. We have two connected shapes. Although the shape consists of different blocks, the final shape is the same. In other words, they are interchangeable. Similarly, expressions can be interchanged with the results of their calculations. How to calculate is not important. What matters is the result:

How to choose

The suggestion I can give is to consider the development specification, coding style and efficiency of the team and weigh the possible problems (such as code block, scope, etc.).

Say something else

return

I suggest adding return to the if statement:

if (someCondition) {
    return resultOfMyCalculation();
}

return resolves the function call to a value and treats the function call as an expression. This will be like variable assignment.

Ternary optimization

If your ternary is very long, it is recommended to split it:

const ten     = Ratio.fromPair(10, 1);
const maxYVal = Ratio.fromNumber(Math.max(...yValues));
const minYVal = Ratio.fromNumber(Math.min(...yValues));

// Create four variables
const rangeEmpty = maxYVal.minus(minYVal).isZero();
const roundRange = ten.pow(maxYVal.minus(minYVal).floorLog10());
const zeroRange  = maxYVal.isZero() ? Ratio.one : maxYVal;
const defaultRng = ten.pow(maxYVal.plus(zeroRange).floorLog10());

// Combine
const yAxisRange = !rangeEmpty ? roundRange : defaultRng;

If you think this results in more variable declarations, you can do this:

const ten     = Ratio.fromPair(10, 1);
const maxYVal = Ratio.fromNumber(Math.max(...yValues));
const minYVal = Ratio.fromNumber(Math.min(...yValues));

// Create two functions
const rangeEmpty = maxYVal.minus(minYVal).isZero();
const roundRange = () => ten.pow(maxYVal.minus(minYVal).floorLog10());
const defaultRng = () => {
    const zeroRange  = maxYVal.isZero() ? Ratio.one : maxYVal;
    return ten.pow(maxYVal.plus(zeroRange).floorLog10());
};

// Combine
const yAxisRange = !rangeEmpty ? roundRange() : defaultRng();

But to be honest, I prefer switch case for too many ternary nested

Related links

Original link

Original translation plan

Keywords: Javascript Front-end

Added by IceRegent on Sun, 09 Jan 2022 17:11:52 +0200