写点什么

编写高质量可维护的代码之优化逻辑判断

  • 2021-04-09
  • 本文字数:5858 字

    阅读完需:约 19 分钟

编写高质量可维护的代码之优化逻辑判断

if else、switch case 是日常开发中最常见的条件判断语句,这种看似简单的语句,当遇到复杂的业务场景时,如果处理不善,就会出现大量的逻辑嵌套,可读性差并且难以扩展。


编写高质量可维护的代码,我们先从最小处入手,一起来看看在前端开发过程中,可以从哪些方面来优化逻辑判断?


下面我们会分别从 JavaScript 语法和 React JSX 语法两个方面来分享一些优化的技巧。


JavaScript 语法篇

嵌套层级优化


function supply(fruit, quantity) {  const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];  // 条件 1: 水果存在  if(fruit) {    // 条件 2: 属于红色水果    if(redFruits.includes(fruit)) {      console.log('红色水果');      // 条件 3: 水果数量大于 10 个      if (quantity > 10) {        console.log('数量大于 10 个');      }    }  } else {    throw new Error('没有水果啦!');  }}
复制代码


分析上面的条件判断,存在三层 if 条件嵌套。


如果提前 return 掉无效条件,将 if else 的多重嵌套层次减少到一层,更容易理解和维护。


function supply(fruit, quantity) {  const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];  if(!fruit) throw new Error('没有水果啦');     // 条件 1: 当 fruit 无效时,提前处理错误  if(!redFruits.includes(fruit)) return; // 条件 2: 当不是红色水果时,提前 return      console.log('红色水果');      // 条件 3: 水果数量大于 10 个  if (quantity > 10) {    console.log('数量大于 10 个');  }}
复制代码


多条件分支的优化处理


当需要枚举值处理不同的业务分支逻辑时,第一反应是写下 if else ?我们来看一下:


function pick(color) {  // 根据颜色选择水果  if(color === 'red') {    return ['apple', 'strawberry'];   } else if (color === 'yellow') {    return ['banana', 'pineapple'];  } else if (color === 'purple') {    return ['grape', 'plum'];  } else {    return [];  }}
复制代码


在上面的实现中:


  • if else 分支太多

  • if else 更适合于条件区间判断,而 switch case 更适合于具体枚举值的分支判断


使用 switch case 优化上面的代码后:


function pick(color) {  // 根据颜色选择水果  switch (color) {    case 'red':      return ['apple', 'strawberry'];    case 'yellow':      return ['banana', 'pineapple'];    case 'purple':      return ['grape', 'plum'];    default:      return [];  }}
复制代码


switch case 优化之后的代码看上去格式整齐,思路很清晰,但还是很冗长。继续优化:


  • 借助 Object 的 { key: value } 结构,我们可以在 Object 中枚举所有的情况,然后将 key 作为索引,直接通过 Object.key 或者 Object[key] 来获取内容


const fruitColor = {                          red: ['apple', 'strawberry'],  yellow: ['banana', 'pineapple'],  purple: ['grape', 'plum'],}function pick(color) {  return fruitColor[color] || [];}
复制代码


  • 使用 Map 数据结构,真正的 (key, value) 键值对结构;


const fruitColor = new Map().set('red', ['apple', 'strawberry']).set('yellow', ['banana', 'pineapple']).set('purple', ['grape', 'plum']);
function pick(color) {  return fruitColor.get(color) || [];}
复制代码


优化之后,代码更简洁、更容易扩展。


为了更好的可读性,还可以通过更加语义化的方式定义对象,然后使用 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 pick(color) {  return fruits.filter(f => f.color == color);}
复制代码


使用数组新特性简化逻辑判断


巧妙的利用 ES6 中提供的数组新特性,也可以让我们更轻松的处理逻辑判断。


多条件判断


编码时遇到多个判断条件时,本能的写下下面的代码(其实也是最能表达业务逻辑的面向过程编码)。


function judge(fruit) {  if (fruit === 'apple' || fruit === 'strawberry' || fruit === 'cherry' || fruit === 'cranberries' ) {    console.log('red');  }}
复制代码


但是当 type 未来到 10 种甚至更多时, 我们只能继续添加 || 来维护代码么?


试试 Array.includes ~


// 将判断条件抽取成一个数组const redFruits = ['apple', 'strawberry', 'cherry', 'cranberries'];function judge(type) {  if (redFruits.includes(fruit)) {    console.log('red');  }}
复制代码

判断数组中是否所有项都满足某条件


const fruits = [  { name: 'apple', color: 'red' },  { name: 'banana', color: 'yellow' },  { name: 'grape', color: 'purple' }];
function match() {  let isAllRed = true;
  // 判断条件:所有的水果都必须是红色  for (let f of fruits) {    if (!isAllRed) break;    isAllRed = (f.color === 'red');  }
  console.log(isAllRed); // false}
复制代码


上面的实现中,主要是为了处理数组中的所有项都符合条件。


使用 Array.every 可以很容的实现这个逻辑:


const fruits = [  { name: 'apple', color: 'red' },  { name: 'banana', color: 'yellow' },  { name: 'grape', color: 'purple' }];
function match() {  // 条件:所有水果都必须是红色  const isAllRed = fruits.every(f => f.color == 'red');
  console.log(isAllRed); // false}
复制代码


判断数组中是否有某一项满足条件


Array.some,它主要处理的场景是判断数组中是否有一项满足条件。

如果想知道是否有红色水果,可以直接使用 Array.some 方法:


const fruits = [    { name: 'apple', color: 'red' },    { name: 'banana', color: 'yellow' },    { name: 'grape', color: 'purple' }  ];
// 条件:是否有红色水果 const isAnyRed = fruits.some(f => f.color == 'red');
复制代码


还有许多其他数组新特性,比如 Array.find、Array.slice、Array.findIndex、Array.reduce、Array.splice 等,在实际场景中可以根据需要选择使用。


函数默认值


使用默认参数


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


我们经常需要处理函数内部的一些参数默认值,上面的代码大家都不陌生,使用函数的默认参数,可以很好的帮助处理这种场景。


const buyFruit = (fruit,amount = 1) => {  if(!fruit){    return  }  console.log(amount,'amount')}
复制代码


我们可以通过 Babel 的转译来看一下默认参数是如何实现的。



从上面的转译结果可以发现,只有参数为 undefined 时才会使用默认参数。

测试的执行结果如下:


buyFruit('apple','');  // amountbuyFruit('apple',null);  //null amountbuyFruit('apple');  //1 amount
复制代码


所以使用默认参数的情况下,我们需要注意的是默认参数 amount = 1 并不等同于 amount || 1


使用解构与默认参数


当函数参数是对象时,我们可以使用解构结合默认参数来简化逻辑。

Before:

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

After:

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

复杂数据解构


当处理比较简的对象时,解构与默认参数的配合是非常好的,但在一些复杂的场景中,我们面临的可能是更复杂的结构。

const oneComplexObj = { firstLevel: {   secondLevel:[{     name:"",     price:""   }]  }}
复制代码

这个时候如果再通过解构去获取对象里的值。

const {  firstLevel:{    secondLevel:[{name,price]=[]  }={}} = oneComplexObj;              
复制代码


可读性就会比较差,而且需要考虑多层解构的默认值以及数据异常情况。


这种情况下,如果项目中使用 lodash 库,可以使用其中的 lodash/get 方法。


import lodashGet from 'lodash/get';
const { name,price} = lodashGet(oneComplexObj,'firstLevel.secondLevel[0]',{});
复制代码


策略模式优化分支逻辑处理


策略模式:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。


使用场景:策略模式属于对象行为模式,当遇到具有相同行为接口、行为内部不同逻辑实现的实例对象时,可以采用策略模式;或者是一组对象可以根据需要动态的选择几种行为中的某一种时,也可以采用策略模式;这里以第二种情况作为示例:


Before:

const TYPE = {  JUICE:'juice',  SALAD:'salad',  JAM:'jam'}function enjoy({type = TYPE.JUICE,fruits}){  if(!fruits || !fruits.length) {    console.log('请先采购水果!');    return; }  if(type === TYPE.JUICE) {    console.log('榨果汁中...');    return '果汁';  }  if(type === TYPE.SALAD) {    console.log('做沙拉中...');    return '拉沙';  }  if(type === TYPE.JAM) {    console.log('做果酱中...');    return '果酱';  }  return;}
enjoy({type:'juice',fruits});
复制代码


使用思路:定义策略对象封装不同行为、提供策略选择接口,在不同的规则时调用相应的行为。

After:


const TYPE = {  JUICE:'juice',  SALAD:'salad',  JAM:'jam'}
const strategies = { [TYPE.JUICE]: function(fruits){ console.log('榨果汁中...'); return '果汁'; }, [TYPE.SALAD]:function(fruits){ console.log('做沙拉中...'); return '沙拉'; }, [TYPE.JAM]:function(fruits){ console.log('做果酱中...'); return '果酱'; },}
function enjoy({type = TYPE.JUICE,fruits}) { if(!type) { console.log('请直接享用!'); return; } if(!fruits || !fruits.length) { console.log('请先采购水果!'); return; } return strategies[type](fruits);}
enjoy({type:'juice',fruits});
复制代码


框架篇之 React JSX 逻辑判断优化


JSX 是一个看起来很像 XML 的 JavaScript 语法扩展。一般在 React 中使用 JSX 来描述界面信息,ReactDOM.render() 将 JSX 界面信息渲染到页面上。


在 JSX 中支持 JavaScript 表达式,日常很常见的循环输出子组件、三元表达式判断、再复杂一些直接抽象出一个函数。


在 JSX 中写这么多  JavaScript 表达式,整体代码看起来会有点儿杂乱。试着优化一下!


JSX-Control-Statements


JSX-Control-Statements (https://www.npmjs.com/package/jsx-control-statements) 是一个 Babel 插件,它扩展了 JSX 的能力,支持以标签的形式处理条件判断、循环。


If 标签


标签内容只有在 condition 为 true 时才会渲染,等价于最简单的三元表达式。


Before:

{ condition() ? 'Hello World!' : null }   
复制代码

After:

<If condition={ condition() }>Hello World!</If>   
复制代码

注意:已被废弃,复杂的条件判断可以使用标签。


Choose 标签


标签下包括至少一个标签、可选的标签。


标签内容只有在 condition 为 true 时才会渲染,相当于一个 if 条件判断分支。


标签则相当于最后的 else 分支。


Before:

{ test1 ? <span>IfBlock1</span> : test2 ? <span>IfBlock2</span> : <span>ElseBlock</span> }
复制代码

After:

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


For 标签


标签需要声明 of、each 属性。


of 接收的是可以使用迭代器访问的对象。


each 代表迭代器访问时的当前指向元素。


Before:

{  (this.props.items || []).map(item => {    return <span key={ item.id }>{ item.title }</span>  })}
复制代码

After:

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

注意:标签不能作为根元素。


With 标签


标签提供变量传参的功能。


Before:

renderFoo = (foo) => {  return <span>{ foo }</span>;}
// JSX 中表达式调用{  this.renderFoo(47)}
复制代码

After:

<With foo={ 47 }>  <span>{ foo }</span></With>
复制代码


使用这几种标签优化代码,可以减少  JSX 中存在的显式 JavaScript 表达式,使我们的代码看上去更简洁,但是这些标签封装的能力,在编译时需要转换为等价的 JavaScript 表达式。


注意:具体 babel-plugin-jsx-control-statements 插件的使用见第三篇参考文章;Vue 框架已经通过指令的形式支持 v-if、v-else-if、v-else、v-show、slot 等。


总结


以上我们总结了一些常见的逻辑判断优化技巧。当然,编写高质量可维护的代码,除了逻辑判断优化,还需要有清晰的注释、含义明确的变量命名、合理的代码结构拆分、逻辑分层解耦、以及更高层次的贴合业务的逻辑抽象等等,相信各位在这方面也有自己的一些心得,欢迎一起留言讨论~



头图:Unsplash

作者:米亚

原文:https://mp.weixin.qq.com/s/S8nBOuu1W8RaXcdMN5U-yQ

原文:编写高质量可维护的代码之优化逻辑判断

来源:政采云前端团队 - 微信公众号 [ID:Zoo-Team]

转载:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2021-04-09 18:085933

评论 4 条评论

发布
用户头像
很赞,也是我平时经常用的优化方法~
2021-06-02 11:33
回复
用户头像
增加了新人阅读成本
2021-05-06 09:57
回复
用户头像
赞!这才是真实的代码
2021-04-19 15:54
回复
用户头像
牛啊
2021-04-17 18:00
回复
没有更多了
发现更多内容

linux之rename命令

入门小站

【国产化替代专题】星环科技春季新品发布周

星环科技

都是分布式操作系统,Laxcus和鸿蒙有何不同?

LAXCUS分布式操作系统

分布式计算 分布式存储 集群架构 鸿蒙系统 分布式操作系统

模块3 作业

KennyQ

尤达 DDD 领域驱动设计思想课程总结

代廉洁

尤达DDD领域驱动设计思想

Java中高级核心知识全面解析——Linux基本命令

爱好编程进阶

Java 面试 后端开发

模块8-设计消息队列存储消息数据的 MySQL 表格

卡西毛豆静爸

#架构实战营

消息队列数据存储表设计

随欣所遇

架构训练营5期

市场进展不断,STI 包括ZB等一系列上线预示着什么?

BlockChain先知

作业八

Geek_f3e842

架构实战营

模块8作业

Mr小公熊

爱讲故事的计算机科学家,和他的分布式系统

多颗糖

GitOps多环境部署问题及解决方案

俞凡

研发效能 gitops

商业分析:SheIn是怎样成功的?

石云升

跨境电商 商业分析 4月月更

架构实战营:模块八作业

刘璐

市场进展不断,STI 包括ZB等一系列上线预示着什么?

西柚子

性能分析优化的道与术

老张

性能优化 性能分析

【架构学习08】——设计消息队列存储消息数据的 MySQL 表格

tiger

架构实战营

Gitlab Java API 使用示例

Java gitlab 4月月更

消息队列存储消息数据的 MySQL 表格

阿卷

架构实战营

微信小程序开发设计需要注意的五个点

源字节1号

前端 后端 软件开发 小程序开发

你好spring-cloud-kubernetes

程序员欣宸

4月月更

开疆作剑,开荒为犁:2022春天,文心大模型走进产业的百花深处

脑极体

统计代码耗时的工具

Rubble

4月日更 4月月更

消息队列存储消息数据的 MySQL 表格设计

李大虾

#架构实战营 「架构实战营」

模块八作业:设计消息队列存储消息数据的 MySQL 表格

炎彬

「架构实战营」

Bigdata 作业第七周

Pyel

在线ASCII Banner艺术字生成工具

入门小站

工具

消息队列存储消息数据的mysql表设计

五月雨

架构实战营 「架构实战营」

浅谈项目中的需求管理

秋去冬来春未远

需求管理 需求分析 需求和问题

多系统信息化实施项目注意事项

秋去冬来春未远

数字化 信息化 系统集成 ERP 多系统

编写高质量可维护的代码之优化逻辑判断_语言 & 开发_政采云前端团队_InfoQ精选文章