How to write more beautiful and clean JavaScript conditional statements

Posted by burge124 on Tue, 01 Feb 2022 12:21:48 +0100

Transferred from: Micro reading  https://www.weidianyuedu.com/content/2317459000818.html

When using JavaScript, we often have to write a lot of conditional statements. Here are five tips for writing cleaner and more beautiful conditional statements.

1. Use array Includes to handle multiple conditions

Take chestnuts for example:

// Conditional statement function test (fruit) {if (fruit = = "apple" | fruit = = "strawberry") {console.log ("red");}}

At first glance, it doesn't seem like a big problem. However, what if we want to match more red fruits, such as cherry and cranberry? Do we have to expand this statement with more 𞓜? We can use array Include rewrite the above conditional sentence.

function test(fruit) {  // Extract conditions into the array const redfruits = ["apple", "strawberry", "cherry", "cranberries"]; if (redFruits.includes(fruit)) {    console.log("red");  }}

It makes our code look more tidy.

2. Write less nesting and return as soon as possible. Let's add two conditions to the previous example:

  • If no fruit is provided, an error is thrown.
  • If the quantity of the fruit is greater than 10, print it out.
function test(fruit, quantity) {const redFruits = ["apple", "strawberry", "cherry", "cranberries"];    // Condition 1: fruit must have a value of if (fruit) {/ / condition 2: it must be red if (redfruits. Includes (fruit)) {console.log ("red"); / / condition 3: it must have a large number of if (quantity > 10) {console.log ("big quantity");}}}} else {        thrownewError("No fruit!");    }}//  Test result test(null)// Error report: no fruit test ("apple")// Print: redtest("apple", 20)// Print: red, big quantity

Let's take a closer look at the code above. We have:

  • 1 if/else statement to filter invalid conditions
  • 3-tier if statement nesting (conditions 1, 2 & 3)

Personally, a general rule I follow is to return as soon as possible when an invalid condition is found.

/_ Return as soon as possible when an invalid condition is found _/function test(fruit, quantity) {const redFruits = ["apple", "strawberry", "cherry", "cranberries"];  // Condition 1: throw an error as soon as possible if (!fruit) thrownewError("No fruit!")// Condition 2: it must be red if (redfruits. Includes (fruit)) {console.log ("red"); / / condition 3: it must be a large number of if (quantity > 10) {console.log ("big quantity");}}}

In this way, we write one less layer of nesting. This is a good code style, especially when the if statement is very long (imagine that you have to scroll to the bottom to know if there is an else statement there. Isn't it a little uncomfortable). If we reverse the conditions, we can further reduce the nesting level. Pay attention to the following condition 2 statement to see how to do this:

/_ Return as soon as possible when invalid conditions are found _/function test(fruit, quantity) {const redFruits = ["apple", "strawberry", "cherry", "cranberries"];  if (!fruit) thrownewError("No fruit!"); // Condition 1: throw the error if (!redFruits.includes(fruit)) return as soon as possible// Condition 2: when the fruit is not red, directly return to console log("red");  //  Condition 3: there must be a large number of if (quantity > 10) {console.log ("big quantity");}}

By reversing the condition of condition 2, our code is no longer nested. This technique works well when the logical chain of our code is very long and we hope that the subsequent process will not be executed when a certain condition is not met. However, there are no hard and fast rules that require you to do so. It depends on yourself. Is this version of code (not nested) better and more readable for you than the previous version (condition 2 nested)? If it is me, I will choose the previous version (condition 2 is nested). The reason is:

  • Such code is relatively short and straightforward, and a nested if makes the structure clearer.

  • Condition reversal leads to more thinking processes (increasing cognitive burden).

Therefore, always pursue less nesting and return earlier, but don't overdo it. If you are interested, here is an article on this issue and a discussion on StackOverflow:

  • Avoid Else, Return Early by Tim Oxley

  • StackOverflow discussion on if/else coding style

3. Using function default parameters and deconstruction, I guess you may be familiar with the following code. In JavaScript , we often need to check null / undefined and give default values:

function test(fruit, quantity) {  if (!fruit) return;const q = quantity || 1; // If quantity is not provided, it defaults to 1 console log(`We have ${q}${fruit}!`);}// Test results ("banana")// We have 1 banana! test("apple", 2); //  We have 2 apple!

In fact, we can remove the variable q through the default parameters of the function.

function test(fruit, quantity = 1) { // If quantity is not provided, the default value is 1 if (! Fruit) return; console. log(`We have ${quantity}${fruit}!`);}// Test results ("banana")// We have 1 banana! test("apple", 2); //  We have 2 apple!

Is it simpler and more straightforward? Note that all function parameters can have their default values. For example, we can also give fruit a default value: function test(fruit = 'unknown', quantity = 1). What if fruit is an Object? Can we still use default parameters?

function test(fruit) {  // If there is a value, print out if (fruit & & fruit. Name) {console.log (fruit. Name);} else {    console.log("unknown");  }}// Test (undefined)// unknowntest({ }); //  unknowntest({ name: "apple", color: "red" }); //  apple

Observe the above example. When the fruit name attribute exists, we want to print it, otherwise print "unknown".

We can avoid writing fruit & & Fruit by default parameters and deconstruction assignment Name this condition.

// Deconstruction - only get the name attribute / / the default parameter is null object {} function test ({name} = {}) {console.log (name | "unknown");}// Test (undefined)// unknowntest({ }); //  unknowntest({ name: "apple", color: "red" }); //  apple

Since we only need the name attribute of fruit, we can use {name} to deconstruct it, and then we can use the name variable to replace fruit in the code name.

We also use {} as its default. If we don't do this, you will get an error cannot structure property name of 'undefined' or 'null' when executing test(undefined), Because there is no name attribute on undefined.

This is not accurate because deconstruction only applies to objects, not because there is no name attribute on undefined (nor on empty objects). Reference deconstruction assignment - MDN)

If you don't mind using third-party libraries, there are some ways to help reduce null checking:

  • Use the # Lodash get # function

  • Use Facebook's open source {idx} Library (with Babeljs)

Here is an example of using Lodash:

//  Using the provided by lodash Library_ Method function test (fruit) {console.log (. Get (fruit, "name", "unknown"); / / get the value of the attribute name. If not, set it to the default value of unknown} / / test result test (undefined)// unknowntest({ }); //  unknowntest({ name: "apple", color: "red" }); //  apple

You can run the demo code here. In addition, if you prefer functional programming (FP), you can choose to use {Lodash fp - the functional version of Lodash (the method name changes to get or getOr).

4. Map / Object may be a better choice than switch

Let's look at the following example. We want to print various fruits according to color:

function test(color) {  // Use switch case to find the fruit with the corresponding color. Switch (color) {case "red": return ["apple", "strawberry"]; case "yellow": return ["banana", "pine"]; case "purple": return ["grape", "plum"]; default: return [];}}// Test result test (null)// []test("yellow"); //  ["banana", "pineapple"]

The above code doesn't look wrong, but personally, it looks lengthy. The same result can be achieved by object literal, and the syntax is more concise:

// Use the literal value of the object to find the fruit of the corresponding color const fruitcolor = {Red: ["apple", "strawberry"], yellow: ["banana", "pine app"], purple: ["grape", "plum"]}; function test(color) {  return fruitColor[color] || [];}

Alternatively, you can use Map to achieve the same effect:

// Use Map to find the fruit with the corresponding color const fruitcolor = newmap() set("red", ["apple", "strawberry"])    . set("yellow", ["banana", "pineapple"])    . set("purple", ["grape", "plum"]); function test(color) {  return fruitColor.get(color) || [];}

Map is a new object type introduced in ES2015, which allows you to store key value pairs. Does that mean we should prohibit the use of switch statements? Don't limit yourself. I will use object literals whenever possible, but that doesn't mean I don't need switch. It depends on the scene. Todd Motto has an article that discusses switch statements and object literals in depth. You may want to see it.

Lazy version: Refactoring syntax is just like the above example. In fact, we can use array by refactoring our code Filter achieves the same effect.

const fruits = [    { name: "apple", color: "red" },    { name: "strawberry", color: "red" },    { name: "banana", color: "yellow" },    { name: "pineapple", color: "yellow" },    { name: "grape", color: "purple" },    { name: "plum", color: "purple" }];function test(color) {  // Use the Array filter to find the fruit with the corresponding color return fruits filter(f => f.color == color);}

There is always more than one way to solve the problem. For this example, we show four implementation methods. Coding is fun!

5. Use array Every and array Some to deal with all / part of the conditions

The last tip is more about using new (not very new) JavaScript} array functions to reduce the number of lines of code. Looking at the following code, we want to check whether all fruits are red:

const fruits = [    { name: "apple", color: "red" },    { name: "banana", color: "yellow" },    { name: "grape", color: "purple" }  ];function test() {  let isAllRed = true;  // Conditions: all fruits must be red for (Let f of fruits) {if (! Isallred) break; isallred = (f.color = = "red");} console. log(isAllRed); //  false}

This code is too long! We can use array Every to reduce the code.

const fruits = [    { name: "apple", color: "red" },    { name: "banana", color: "yellow" },    { name: "grape", color: "purple" }  ];function test() {  // Conditions: (short form) all fruits must be red const isallred = fruits every(f => f.color == "red");   console. log(isAllRed); //  false}

Much clearer, right? Similarly, if we want to check whether at least one fruit is red, we can use array Some is implemented in just one line of code.

const fruits = [    { name: "apple", color: "red" },    { name: "banana", color: "yellow" },    { name: "grape", color: "purple" }];function test() {  // Condition: at least one fruit is red const isanyred = fruits some(f => f.color == "red");   console. log(isAnyRed); //  true}

summary

Let's write more readable code together. I hope this article can bring you some help.

Topics: Javascript Programming ECMAScript