This is the 64th unwatered original, want to get more original good articles, please search the public number to pay attention to us ~ this article was first published in the political cloud front blog: Write high quality maintainable code: logical judgment

preface

If else and Switch case are the most common conditional judgment statements in daily development. Such seemingly simple statements, when encountering complex business scenarios, will appear a lot of logic nesting, poor readability and difficult to expand if not handled properly.

In order to write high quality maintainable code, let’s start with the minimum. Let’s look at what aspects of the front-end development process can be optimized for logical judgment.

Here are some tips on how to optimize JavaScript syntax and React JSX syntax.

JavaScript syntax article

Nesting level optimization

function supply(fruit, quantity) {
  const redFruits = ['apple'.'strawberry'.'cherry'.'cranberries'];
  // Condition 1: fruit exists
  if (fruit) {
    // Condition 2: a red fruit
    if (redFruits.includes(fruit)) {
      console.log('Red fruit');
      // Condition 3: more than 10 fruits
      if (quantity > 10) {
        console.log('Quantity > 10'); }}}else {
    throw new Error('No fruit! '); }}Copy the code

According to the above condition judgment, there are three levels of nesting if conditions.

If you return invalid conditions early, you reduce the multiple nesting levels of if else to a single level, making it easier to understand and maintain.

function supply(fruit, quantity) {
  const redFruits = ['apple'.'strawberry'.'cherry'.'cranberries'];
  if(! fruit)throw new Error('There's no fruit.'); // Condition 1: Preprocess error when fruit is invalid
  if(! redFruits.includes(fruit))return; // Condition 2: return in advance when the fruit is not red
  
  console.log('Red fruit');
  
  // Condition 3: more than 10 fruits
  if (quantity > 10) {
    console.log('Quantity > 10'); }}Copy the code

Optimization of multi-conditional branch

When an enumeration value is needed to handle different business branch logic, the first response is to write if else? Let’s take a look:

function pick(color) {
  // Select the fruit according to the color
  if(color === 'red') {
      return ['apple'.'strawberry']; 
  } else if (color === 'yellow') {
      return ['banana'.'pineapple'];
  } else if (color === 'purple') {
      return ['grape'.'plum'];
  } else {
      return[]; }}Copy the code

In the above implementation:

  • If else branches too much
  • If else is more suitable for conditional interval judgments, while Switch case is more suitable for branching judgments of specific enumeration values

After optimizing the above code with switch Case:

function pick(color) {
  // Select the fruit according to the color
  switch (color) {
    case 'red':
      return ['apple'.'strawberry'];
    case 'yellow':
      return ['banana'.'pineapple'];
    case 'purple':
      return ['grape'.'plum'];
    default:
      return[]; }}Copy the code

The optimized switch Case code looks neat and clear, but it’s still very verbose. Continue to optimize:

  • Using the {key: value} structure of an Object, we can enumerate all cases in an Object, and then use the key as the index to get the content directly from object. key or Object[key]
const fruitColor = {
  red: ['apple'.'strawberry'].yellow: ['banana'.'pineapple'].purple: ['grape'.'plum']};function pick(color) {
  return fruitColor[color] || [];
}
Copy the code
  • Using Map data structure, real (key, value) key-value pair structure;
const fruitColor = new Map()
.set('red'['apple'.'strawberry'])
.set('yellow'['banana'.'pineapple'])
.set('purple'['grape'.'plum']);

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

After optimization, the code is cleaner and easier to extend.

For better readability, you can define objects more semantically and then use array.filter to achieve 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 pick(color) {
  return fruits.filter(f= > f.color === color);
}
Copy the code

Use new array features to simplify logical decisions

Using the new array feature provided in ES6, we can also handle logic more easily.

Multiconditional judgment

When you encounter multiple criteria while coding, you instinctively write the following code (which is actually the most process-oriented code that expresses business logic).

function judge(fruit) {
  if (fruit === 'apple' || fruit === 'strawberry' || fruit === 'cherry' || fruit === 'cranberries' ) {
    console.log('red'); }}Copy the code

But when the type in the future to 10 kinds of even more, we can only continue to add | | to maintain the code?

Try Array. Includes ~

// Extract the criteria into an array
const redFruits = ['apple'.'strawberry'.'cherry'.'cranberries'];
function judge(fruit) {
  if (redFruits.includes(fruit)) {
      console.log('red'); }}Copy the code

Determines whether all items in the array meet certain conditions

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

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

  console.log(isAllRed); // false
}

Copy the code

In the above implementation, the main purpose is to handle that all the items in the array are qualified.

Use Array. Every to implement this logic:

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

  console.log(isAllRed); // false
}

Copy the code

Determine whether any item in the array satisfies the condition

Array.some, which mainly deals with the scenario of determining whether an item in the Array satisfies a condition.

If you want to know if there is any red fruit, use the array. some method:

const fruits = [
  { name: 'apple'.color: 'red' },
  { name: 'banana'.color: 'yellow' },
  { name: 'grape'.color: 'purple'}];// Condition: whether there is red fruit
const isAnyRed = fruits.some(f= > f.color == 'red');

Copy the code

There are many new Array features, such as array. find, array. slice, array. findIndex, array. reduce, and array. splice, which can be used as required in actual scenarios.

Function default

Use default parameters

const buyFruit = (fruit,amount) = > {
 if(! fruit){return
  }
  amount = amount || 1;
  console.log(amount)
}

Copy the code

We often need to deal with some parameters inside the function default values, the above code is familiar, using the function default parameters, can be a good help to deal with such scenarios.

const buyFruit = (fruit,amount = 1) = > {
 if(! fruit){return
  }
  console.log(amount,'amount')}Copy the code

We can see how the default parameters are implemented through Babel translation.

As you can see from the above translation, the default parameter is only used when the parameter is undefined.

The test results are as follows:

buyFruit('apple'.' ');  // amount
buyFruit('apple'.null);  //null amount
buyFruit('apple');  //1 amount

Copy the code

So under the condition of using the default parameters, we need to pay attention to is the default parameter amount = 1 is not the same as the amount | | 1.

Use deconstruction with default parameters

When function arguments are objects, we can use destructuring with default arguments to simplify the logic.

Before:

const buyFruit = (fruit,amount) = > {
  fruit = fruit || {};
  if(! fruit.name || ! fruit.price){return; }... amount = amount ||1;
  console.log(amount)
}

Copy the code

After:

const buyFruit = ({ name,price }={},amount) = > {
  if(! name || ! prices){return;
  }
  console.log(amount)
}

Copy the code

Complex data deconstruction

Deconstruction works well with default parameters when dealing with simpler objects, but in some complex scenarios we may be faced with more complex structures.

const oneComplexObj = {
  firstLevel: {
    secondLevel: [{
      name: ' '.price: ' ',}].}};Copy the code

At this time, if you use deconstruction to get the value in the object.

const {
  firstLevel: {
    secondLevel: [{ name, price }] = [],
  } = {},
} = oneComplexObj;
Copy the code

Readability is poor, and you need to take into account the default values for multiple layers of deconstruction and data exceptions.

In this case, if your project uses the LOdash library, you can use the LOdash/GET method in it.

import lodashGet from 'lodash/get';

const { name,price } = lodashGet(oneComplexObj,'firstLevel.secondLevel[0]'{});Copy the code

The policy pattern optimizes branching logic processing

Strategy pattern: Define a set of algorithms, encapsulate them one by one, and make them interchangeable.

Usage scenario: The policy mode belongs to the object behavior mode. When encountering instance objects with the same behavior interface and different logic implementations within the behavior, the policy mode can be adopted. Or when a group of objects can dynamically select one of several behaviors according to the needs, the policy mode can also be adopted; Here’s the second case as an example:

Before:

const TYPE = {
  JUICE: 'juice'.SALAD: 'salad'.JAM: 'jam'};function enjoy({ type = TYPE.JUICE, fruits }) {
  if(! fruits || ! fruits.length) {console.log('Please buy fruit first! ');
    return;
  }
  if (type === TYPE.JUICE) {
    console.log('In juice... ');
    return 'juice';
  }
  if (type === TYPE.SALAD) {
    console.log('In the salad... ');
    return 'lassa';
  }
  if (type === TYPE.JAM) {
    console.log('In making jam... ');
    return 'jam';
  }
}

enjoy({ type: 'juice', fruits });
Copy the code

Use idea: define policy objects to encapsulate different behaviors, provide interfaces for policy selection, and invoke corresponding behaviors when different rules are used.

After:

const TYPE = {
  JUICE: 'juice'.SALAD: 'salad'.JAM: 'jam'};const strategies = {
  [TYPE.JUICE](fruits) {
    console.log('In juice... ');
    return 'juice';
  },
  [TYPE.SALAD](fruits) {
    console.log('In the salad... ');
    return 'salad';
  },
  [TYPE.JAM](fruits) {
    console.log('In making jam... ');
    return 'jam'; }};function enjoy({ type = TYPE.JUICE, fruits }) {
  if(! type) {console.log('Please enjoy directly! ');
    return;
  }
  if(! fruits || ! fruits.length) {console.log('Please buy fruit first! ');
    return;
  }
  return strategies[type](fruits);
}

enjoy({ type: 'juice', fruits });
Copy the code

React JSX logic judgment optimization in framework

JSX is a JavaScript syntax extension that looks a lot like XML. JSX is usually used in React to describe interface information. Reactdom.render () renders JSX interface information onto the page.

JavaScript expressions are supported in JSX, the usual routine of looping out subcomponents, ternary expression judgments, and more complex abstractions to a function.

Writing so many JavaScript expressions in JSX can make the overall code look a little messy. Try to optimize!

JSX-Control-Statements

Jsx-control-statements is a Babel plug-in that extends the capabilities of JSX to handle conditional judgments, loops in the form of labels.

If the label

The tag content is rendered only if the condition is true, equivalent to the simplest ternary expression.

Before:

{ condition() ? 'Hello World! ' : null }   

Copy the code

After:

<If condition={ condition() }>Hello World! </If>Copy the code

Note: Deprecated, tags can be used for complex conditional judgments.

Choose the tag

The tag contains at least one tag and optional tags.

The tag contents are rendered only if the condition is true, which acts as an if branch.

The tag is the final else branch.

Before:

{ test1 ? <span>IfBlock1</span> : test2 ? <span>IfBlock2</span> : <span>ElseBlock</span> }

Copy the code

After:

<Choose>
  <When condition={ test1} >
    <span>IfBlock1</span>
  </When>
  <When condition={ test2} >
    <span>IfBlock2</span>
  </When>
  <Otherwise>
    <span>ElseBlock</span>
  </Otherwise>
</Choose>

Copy the code

For the label

Tags need to declare the of and each attributes.

Of receives objects that can be accessed using iterators.

Each represents the element to which the iterator is currently referring.

Before:

{(this.props.items || []).map(item= > {
      return <span key={ item.id} >{ item.title }</span>})}Copy the code

After:

<For each="item" of= {this.props.items }>
   <span key={ item.id} >{ item.title }</span>
</For>

Copy the code

Note: Tags cannot be used as root elements.

With the label

The tag provides the ability to pass variables.

Before:

renderFoo = (foo) = > {
    return <span>{ foo }</span>;
}

// Expression calls in JSX
{
    this.renderFoo(47)}Copy the code

After:

<With foo={ 47} ><span>{ foo }</span>
</With>

Copy the code

Using these tags to optimize code can reduce the explicit JavaScript expressions present in JSX and make our code look cleaner, but the ability to encapsulate these tags needs to be converted to equivalent JavaScript expressions at compile time.

Note: See the third reference article for the specific use of the Babel-Plugin-JsX-Control-Statements plug-in; The Vue framework already supports v-if, V-else-if, V-else, V-show, slot, etc., in the form of instructions.

conclusion

Above we have summarized some common logic judgment optimization techniques. High quality maintainable code, of course, in addition to logic optimization, also need a clear annotation, meaning clear variable naming, the reasonable structure of the code, split, logical layering decoupling, and a higher level of joint business logic abstraction and so on, believe you also have some of his own experience in this respect, welcome message to discuss together ~

reference

  • 5 Tips to Write Better Conditionals in JavaScript
  • stop-putting-so-many-if-statements-in-your-javascript
  • JSX Control Statements

Recommended reading

Teach you to build enterprise-class NPM private warehouse in minutes

A list of Git exception handlers worth keeping

, recruiting

ZooTeam (ZooTeam), a young and creative team, belongs to the product RESEARCH and development department of ZooTeam, based in picturesque Hangzhou. The team now has more than 50 front end partners, the average age of 27 years old, nearly 30% are full stack engineers, no problem youth storm team. The membership consists of “old” soldiers from Alibaba and netease, as well as fresh graduates from Zhejiang University, University of Science and Technology of China, Hangzhou Electric And other universities. In addition to the daily business connection, the team also carried out technical exploration and actual practice in the direction of material system, engineering platform, building platform, performance experience, cloud application, data analysis and visualization, promoted and implemented a series of internal technical products, and continued to explore the new boundary of the front-end technology system.

If you want to change the things you’ve been doing, you want to start doing things. If you want to change, you’ve been told you need to think more, but you can’t change; If you want to change, you have the power to achieve that result, but you are not needed; If you want to change what you want to accomplish, you need a team to support you, but there is no place for you to bring people; If you want a change of pace, it’s “3 years of experience in 5 years”; If you want to change the original savvy is good, but there is always a layer of fuzzy window paper… If you believe in the power of belief, that ordinary people can achieve extraordinary things, that you can meet a better version of yourself. If you want to get involved in the growth of a front end team with a deep understanding of the business, a sound technology system, technology that creates value, and spillover impact as the business takes off, I think we should talk about it. Any time, waiting for you to write something, to [email protected]