There are a lot of conditional judgments to deal with when working in JavaScript, so here are five tips to help you write better/clearer conditional statements.

1. Use array.includes in multiple decisions

Let’s take a look at this example:

// condition
function test(fruit) {
  if (fruit == 'apple' || fruit == 'strawberry') {
    console.log('red'); }}Copy the code

At first glance, the above example looks ok. But what if more red fruits, such as cherry and cranberries, were added? You will use more | | to extend the conditional statement?

We can override the above conditional statement with array. includes(array.includes). As follows:

function test(fruit) {
  // extract conditions to array
  const redFruits = ['apple'.'strawberry'.'cherry'.'cranberries'];

  if (redFruits.includes(fruit)) {
    console.log('red'); }}Copy the code

We extract red fruits into an array. By doing so, the code looks cleaner.

2. Less nesting, early return

Let’s extend the above example to include two more conditions:

  • If no fruit argument is passed, an error is thrown
  • Accept the Quantity parameter and print it out if it exceeds 10
function test(fruit, quantity) {
  const redFruits = ['apple'.'strawberry'.'cherry'.'cranberries'];

  // condition 1: fruit must has value
  if (fruit) {
    // condition 2: must be red
    if (redFruits.includes(fruit)) {
      console.log('red');

      // condition 3: must be big quantity
      if (quantity > 10) {
        console.log('big quantity'); }}}else {
    throw new Error('No fruit! '); }}// test results
test(null); // error: No fruits
test('apple'); // print: red
test('apple'.20); // print: red, big quantity
Copy the code

Take a look at the code above and let’s go:

  • 1 if/else statement to screen out invalid conditional statements
  • Layer 3 nested statements (conditions 1,2 and 3)

The rule I personally follow is to return early when an invalid condition is found.

/ _return early when invalid conditions found _/

function test(fruit, quantity) {
  const redFruits = ['apple'.'strawberry'.'cherry'.'cranberries'];

  // condition 1: throw error early
  if(! fruit)throw new Error('No fruit! ');

  // condition 2: must be red
  if (redFruits.includes(fruit)) {
    console.log('red');

    // condition 3: must be big quantity
    if (quantity > 10) {
      console.log('big quantity'); }}}Copy the code

By returning early, we reduce one layer of nested statements. This coding style is great, especially if you have a long if statement (imagine you have to scroll a long way to know there is an else statement, not cool at all).

(for the example above) we can further reduce if nesting by inverting judgment conditions and returning early. Let’s see how we deal with condition 2:

/ _return early when invalid conditions found _/

function test(fruit, quantity) {
  const redFruits = ['apple'.'strawberry'.'cherry'.'cranberries'];

  if(! fruit)throw new Error('No fruit! '); // condition 1: throw error early
  if(! redFruits.includes(fruit))return; // condition 2: stop when fruit is not red

  console.log('red');

  // condition 3: must be big quantity
  if (quantity > 10) {
    console.log('big quantity'); }}Copy the code

By inverting condition 2, we avoid nested statements. This technique is useful when we are dealing with long logic and want to be able to stop when conditions are not met.

And it’s not that hard to do. Ask yourself, is this version (no conditional nesting) better/more readable than the previous version (two-layer nesting)?

However, for my part, I will keep the previous version (containing two layers of nesting). Because:

  • The code is shorter and more direct, and the nesting of ifs is clearer
  • Reversing judgment conditions may increase thinking load (increase cognitive load)

Therefore, nesting should be minimized and return early, but not too much. If you’re interested, you can check out the following article and StackOverflow discussion to learn more:

  • Avoid Else, Return Early by Tim Oxley
  • StackOverflow discussion on if/else coding style

3. Use default parameters and deconstruction

I’m guessing you’re familiar with the following code: in JavaScript we always need to check for null/undefined values and specify default values.

function test(fruit, quantity) {
  if(! fruit)return;
  const q = quantity || 1; // if quantity not provided, default to one

  console.log(`We have ${q} ${fruit}! `);
}

//test results
test('banana'); // We have 1 banana!
test('apple'.2); // We have 2 apple!
Copy the code

In fact, we can eliminate the variable q by declaring the default function parameters.

function test(fruit, quantity = 1) { // if quantity not provided, default to one
  if(! fruit)return;
  console.log(`We have ${quantity} ${fruit}! `);
}

//test results
test('banana'); // We have 1 banana!
test('apple'.2); // We have 2 apple!
Copy the code

It’s easy and intuitive! Note that each declaration has its own default parameters. For example, we could also give fruit a default: function test(fruit = ‘unknown’, quantity = 1).

What if our fruit is an object? Can we assign a default parameter?

function test(fruit) { 
  // printing fruit name if value provided
  if (fruit && fruit.name)  {
    console.log (fruit.name);
  } else {
    console.log('unknown'); }}//test results
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple'.color: 'red' }); // apple
Copy the code

In the above example, we print the fruit name if there is fruit name, otherwise unknown. We can avoid judging the condition fruit && fruit.name by setting default parameters and deconstruction.

// destructing - get name property only
// assign default empty object {}
function test({name} = {}) {
  console.log (name || 'unknown');
}

//test results
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple'.color: 'red' }); // apple
Copy the code

Since we only need the name attribute, we can use {name} to deconstruct the parameter, and then we can use name instead of fruit.name.

We also declare an empty object {} as the default. If we don’t, you’ll get an error that can’t be deconstructed from undefined or NULL. Because undefined has no name property.

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

  • Use the Lodash get function
  • Facebook’s open source library IDX (used with Babeljs)

Here’s an example using Lodash:

// Include lodash library, you will get _
function test(fruit) {
  console.log(__.get(fruit, 'name'.'unknown'); // get property name, if not available, assign default value 'unknown'
}

//test results
test(undefined); // unknown
test({ }); // unknown
test({ name: 'apple'.color: 'red' }); // apple
Copy the code

You can run the demo code in JSBIN, or if you’re a fan of functional programming, you can choose Lodash FP, the functional version of Lodash (change the method to get or getOr).

4. Favor object traversal over switch statements

Looking at the code below, we want to print fruit based on color.

function test(color) {
  // use switch case to find fruits in color
  switch (color) {
    case 'red':
      return ['apple'.'strawberry'];
    case 'yellow':
      return ['banana'.'pineapple'];
    case 'purple':
      return ['grape'.'plum'];
    default:
      return[]; }}//test results
test(null); / / []
test('yellow'); // ['banana', 'pineapple']
Copy the code

The above code looks fine, but it’s somewhat redundant. The syntax looks much cleaner when you implement the same result with object literals:

// use object literal to find fruits in color
  const fruitColor = {
    red: ['apple'.'strawberry'].yellow: ['banana'.'pineapple'].purple: ['grape'.'plum']};function test(color) {
  return fruitColor[color] || [];
}
Copy the code

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

// use Map to find fruits in color
  const fruitColor = new Map()
    .set('red'['apple'.'strawberry'])
    .set('yellow'['banana'.'pineapple'])
    .set('purple'['grape'.'plum']);

function test(color) {
  return fruitColor.get(color) || [];
}
Copy the code

Map is an object type implemented after the ES2015 specification that allows you to store key-value pairs.

So, should we ban switch statements? Don’t limit yourself to doing this. Personally, I use object traversal whenever possible, but don’t stick to it strictly, and use a way that makes more sense for the current scene.

Todd Motto has an in-depth look at switch statements and traversal objects, which you can check out here.

TL; DL; Refactoring grammar

For the example above, we can achieve the same result by refactoring the code below with array.filter.

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 Array filter to find fruits in color

  return fruits.filter(f= > f.color == color);
}
Copy the code

There are more than one way to achieve the same result, and we’ve shown four of them above. Coding is fun!

5. Use array.every/array.some for all/part judgments

The final tip is to use Javascript’s built-in array functions to reduce the number of lines of code. Looking at the code below, we want to see if all the fruit is red:

const fruits = [
    { name: 'apple'.color: 'red' },
    { name: 'banana'.color: 'yellow' },
    { name: 'grape'.color: 'purple'}];function test() {
  let isAllRed = true;

  // condition: all fruits must be red
  for (let f of fruits) {
    if(! isAllRed)break;
    isAllRed = (f.color == 'red');
  }

  console.log(isAllRed); // false
}
Copy the code

The code above is too long! We use array.every to reduce the number of lines of code:

const fruits = [
    { name: 'apple'.color: 'red' },
    { name: 'banana'.color: 'yellow' },
    { name: 'grape'.color: 'purple'}];function test() {
  // condition: short way, all fruits must be red
  const isAllRed = fruits.every(f= > f.color == 'red');

  console.log(isAllRed); // false
}
Copy the code

Much clearer, right? Similarly, we want to test if any fruit is red, which we can do using array.some.

const fruits = [
    { name: 'apple'.color: 'red' },
    { name: 'banana'.color: 'yellow' },
    { name: 'grape'.color: 'purple'}];function test() {
  // condition: if any fruit is red
  const isAnyRed = fruits.some(f= > f.color == 'red');

  console.log(isAnyRed); // true
}
Copy the code

conclusion

Let’s write more readable code together. I hope you can learn something new from this article.

That’s all. Have fun coding!

The original pausing. IO/bar – talk / 5 -…

First article: github.com/reng99/blog…

More: github.com/reng99/blog…