JS 逻辑判断不要只知道用 if-else 和 switch条件判断(小技巧)


Posted in Javascript onMay 27, 2020

我们在编写 JS 代码时,经常会遇到逻辑判断复杂的情况。一般情况下,可以用 if/else 或 switch 来实现多个条件判断,但会出现一个问题:随着逻辑复杂度的增加,代码中的 if/else 和 switch 会越来越臃肿。本文将带你尝试写出更优雅的判断逻辑。

比如说下面这样一段代码:

const onButtonClick = (status) => {
 if (status == 1) {
 sendLog('processing')
 jumpTo('IndexPage')
 } else if (status == 2) {
 sendLog('fail')
 jumpTo('FailPage')
 } else if (status == 3) {
 sendLog('fail')
 jumpTo('FailPage')
 } else if (status == 4) {
 sendLog('success')
 jumpTo('SuccessPage')
 } else if (status == 5) {
 sendLog('cancel')
 jumpTo('CancelPage')
 } else {
 sendLog('other')
 jumpTo('Index')
 }
}

你可以在代码中看到这个按钮的点击逻辑。根据活动状态的不同做两件事,发送日志埋点并跳转到相应的页面。很容易想到这段代码可以用 switch 重写如下:

const onButtonClick = (status) => {
 switch (status) {
 case 1:
 sendLog('processing')
 jumpTo('IndexPage')
 break
 case 2:
 case 3:
 sendLog('fail')
 jumpTo('FailPage')
 break
 case 4:
 sendLog('success')
 jumpTo('SuccessPage')
 break
 case 5:
 sendLog('cancel')
 jumpTo('CancelPage')
 break
 default:
 sendLog('other')
 jumpTo('Index')
 break
 }
}

好吧,看起来比 if/else 层次结构更清晰一些,细心的读者可能也发现了一个小窍门:case 2 和 case 3 的逻辑一样时,可以把前面的逻辑处理代码省略,case 2 会自动执行与 case 3 的逻辑。

不过,还有一个更简单的写法:

const actions = {
 '1': ['processing', 'IndexPage'],
 '2': ['fail', 'FailPage'],
 '3': ['fail', 'FailPage'],
 '4': ['success', 'SuccessPage'],
 '5': ['cancel', 'CancelPage'],
 default: ['other', 'Index'],
}

const onButtonClick = (status) => {
 let action = actions[status] || actions['default'],
 logName = action[0],
 pageName = action[1]
 sendLog(logName)
 jumpTo(pageName)
}

上面的代码看起来确实比较干净,这种方法的巧妙之处在于,它把判断条件作为对象的属性名,把处理逻辑作为对象的属性值。在点击按钮的时候,这种方法特别适用于单项条件判断的情况,即通过对象属性查找的方式进行逻辑判断。

这个方法很好,但是有没有其他的方法来编码呢?有的!

const actions = new Map([
 [1, ['processing', 'IndexPage']],
 [2, ['fail', 'FailPage']],
 [3, ['fail', 'FailPage']],
 [4, ['success', 'SuccessPage']],
 [5, ['cancel', 'CancelPage']],
 ['default', ['other', 'Index']],
])

const onButtonClick = (status) => {
 let action = actions.get(status) || actions.get('default')
 sendLog(action[0])
 jumpTo(action[1])
}

使用 Map 代替 Object 有很多优点,Map 对象和普通对象有的区别是:

  • 一个对象通常有自己的原型,所以一个对象总是有一个“prototype”键
  • 对象的键只能是一个字符串或符号,但 Map 的键可以是任何值
  • 你可以通过使用 size 属性很容易得到 Map 中的键值对的数量,而一个对象中的键值对数量不能直接获取

现在我们来升级一下这个问题的难度。点击按钮时,不仅要判断状态,还要判断用户的身份。

const onButtonClick = (status, identity) => {
 if (identity == 'guest') {
 if (status == 1) {
 //do sth
 } else if (status == 2) {
 //do sth
 } else if (status == 3) {
 //do sth
 } else if (status == 4) {
 //do sth
 } else if (status == 5) {
 //do sth
 } else {
 //do sth
 }
 } else if (identity == 'master') {
 if (status == 1) {
 //do sth
 } else if (status == 2) {
 //do sth
 } else if (status == 3) {
 //do sth
 } else if (status == 4) {
 //do sth
 } else if (status == 5) {
 //do sth
 } else {
 //do sth
 }
 }
}

从上面的例子中可以看到,当你的逻辑升级到双重判断的时候,你的判断力就会加倍,你的代码就会加倍。

如何才能让代码更干净利落呢?

这里有一个解决方案。

const actions = new Map([
 ['guest_1', () => {}],
 ['guest_2', () => {}],
 ['guest_3', () => {}],
 ['guest_4', () => {}],
 ['guest_5', () => {}],
 ['master_1', () => {}],
 ['master_2', () => {}],
 ['master_3', () => {}],
 ['master_4', () => {}],
 ['master_5', () => {}],
 ['default', () => {}],
])

const onButtonClick = (identity, status) => {
 let action = actions.get(`${identity}_${status}`) || actions.get('default')
 action.call(this)
}

上述代码的核心逻辑是。将两个判断条件拼接成一个字符串作为 Map 的键,然后在查询时直接查询对应字符串的值。当然,我们也可以在这里把 Map 改成 Object。

const actions = {
 guest_1: () => {},
 guest_2: () => {},
 //....
}
const onButtonClick = (identity, status) => {
 let action = actions[`${identity}_${status}`] || actions['default']
 action.call(this)
}

如果读者觉得把查询拼成一个字符串有点尴尬,还有另一个解决办法,那就是用一个 Map 对象作为 key。

const actions = new Map([
 [{ identity: 'guest', status: 1 }, () => {}],
 [{ identity: 'guest', status: 2 }, () => {}],
 //...
])
const onButtonClick = (identity, status) => {
 let action = [...actions].filter(([key, value]) => key.identity == identity && key.status == status)
 action.forEach(([key, value]) => value.call(this))
}

这里你也可以看到 Map 和普通对象的区别,其中 Map 可以用任何类型的数据作为键。现在让我们把它的难度再提高一点。如果对于 guest 身份来说,状态 1-4 的处理逻辑是一样的呢?

最坏的情况是这样的(代码大量重复):

const actions = new Map([
 [{ identity: 'guest', status: 1 }, () => {}],
 [{ identity: 'guest', status: 2 }, () => {}],
 [{ identity: 'guest', status: 3 }, () => {}],
 [{ identity: 'guest', status: 4 }, () => {}],
 [{ identity: 'guest', status: 5 }, () => {}],
 //...
])

更好的方法是把处理逻辑函数分离出来:

const actions = () => {
 const functionA = () => {}
 const functionB = () => {}
 return new Map([
 [{ identity: 'guest', status: 1 }, functionA],
 [{ identity: 'guest', status: 2 }, functionA],
 [{ identity: 'guest', status: 3 }, functionA],
 [{ identity: 'guest', status: 4 }, functionA],
 [{ identity: 'guest', status: 5 }, functionB],
 //...
 ])
}

const onButtonClick = (identity, status) => {
 let action = [...actions()].filter(([key, value]) => key.identity == identity && key.status == status)
 action.forEach(([key, value]) => value.call(this))
}

这对于日常需求来说已经足够了,但是说真的,函数 A 被引用了 4 次,还是有点烦人。

如果事情真的变得很复杂,比如身份有 3 种,状态有 10 种,你需要定义 30 个处理逻辑,其中很多处理逻辑都是一样的,这似乎让人无法接受。

而你可以这样做:

const actions = () => {
 const functionA = () => {} // 逻辑处理 A
 const functionB = () => {} // 逻辑处理 B
 return new Map([
 [/^guest_[1-4]$/, functionA],
 [/^guest_5$/, functionB],
 //...
 ])
}

const onButtonClick = (identity, status) => {
 let action = [...actions()].filter(([key, value]) => key.test(`${identity}_${status}`))
 action.forEach(([key, value]) => value.call(this))
}

这时使用 Map 而不是 Object 的优势比较明显,因为可以用正则式作为键。

如果需求变成:所有的对 guest 操作都需要发送一个日志埋点,不同状态的 guest 可能有不同的逻辑处理,那么我们可以写成如下:

const actions = () => {
 const functionA = () => {} // 逻辑处理 A
 const functionB = () => {} // 逻辑处理 B
 const functionC = () => {} // 发送日志 C
 return new Map([
 [/^guest_[1-4]$/, functionA],
 [/^guest_5$/, functionB],
 [/^guest_.*$/, functionC],
 //...
 ])
}

const onButtonClick = (identity, status) => {
 let action = [...actions()].filter(([key, value]) => key.test(`${identity}_${status}`))
 action.forEach(([key, value]) => value.call(this))
}

这样一来,公共逻辑和单个逻辑可以同时执行。

总结

本文讲到了八种 JS 逻辑判断的写法,包括:

  1. if/else
  2. switch
  3. 单一判断:存储在 Object 中
  4. 单一判断:存储在 Map 对象中
  5. 多重判断:将条件串联成一个字符串,存储在 Object 中
  6. 多重判断:将条件连成一个字符串,存储在 Map 对象中
  7. 多重判断:把条件作为对象存储在 Map 中
  8. 多重判断:把条件写成正则式存储在 Map 中

到此这篇关于JS 逻辑判断不要只知道用 if-else 和 switch条件判断的文章就介绍到这了,更多相关js 逻辑判断if else switch内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
javascript与CSS复习(二)
Jun 29 Javascript
目前流行的JavaScript库的介绍及对比
Sep 29 Javascript
JS获取当前网址、主机地址项目根路径
Nov 19 Javascript
javascript数组随机排序实例分析
Jul 22 Javascript
JS+CSS简单树形菜单实现方法
Sep 12 Javascript
jQuery+Ajax+PHP+Mysql实现分页显示数据实例讲解
Sep 27 Javascript
JavaScript编程中布尔对象的基本使用
Oct 25 Javascript
D3.js实现文本的换行详解
Oct 14 Javascript
BootStrap树状图显示功能
Nov 24 Javascript
原生JS实现圆环拖拽效果
Apr 07 Javascript
Vue响应式原理深入解析及注意事项
Dec 11 Javascript
JS Array.from()将伪数组转换成数组的方法示例
Mar 23 Javascript
微信公众号网页分享功能开发的示例代码
May 27 #Javascript
JS字符串补全方法padStart()和padEnd()
May 27 #Javascript
Js生成随机数/随机字符串的方法小结【5种方法】
May 27 #Javascript
JS箭头函数和常规函数之间的区别实例分析【 5 个区别】
May 27 #Javascript
使用JavaScript获取Django模板指定键值数据
May 27 #Javascript
基于Vue CSR的微前端实现方案实践
May 27 #Javascript
Node.js API详解之 vm模块用法实例分析
May 27 #Javascript
You might like
PHP输出控制功能在简繁体转换中的应用
2006/10/09 PHP
Win2003服务器安全加固设置--进一步提高服务器安全性
2007/05/23 PHP
PHP实现货币换算的方法
2014/11/29 PHP
CodeIgniter配置之config.php用法实例分析
2016/01/19 PHP
浅谈PHP中pack、unpack的详细用法
2018/03/12 PHP
jQuery实现form表单reset按钮重置清空表单功能
2012/12/18 Javascript
javascript 实现子父窗体互相传值的简单实例
2014/02/17 Javascript
js通过location.search来获取页面传来的参数
2014/09/11 Javascript
Javascript基础知识(三)BOM,DOM总结
2014/09/29 Javascript
javascript实现点击提交按钮后显示loading的方法
2015/07/03 Javascript
BootStrap的table表头固定tbody滚动的实例代码
2016/08/24 Javascript
HTML5 JS压缩图片并获取图片BASE64编码上传
2020/11/16 Javascript
利用webstrom调试Vue.js单页面程序的方法教程
2017/06/06 Javascript
JavaScript实现简单的树形菜单效果
2017/06/23 Javascript
vue-cli webpack模板项目搭建及打包时路径问题的解决方法
2018/02/26 Javascript
详解在Javascript中进行面向切面编程
2019/04/28 Javascript
Vue解析剪切板图片并实现发送功能
2020/02/04 Javascript
Python简单删除目录下文件以及文件夹的方法
2015/05/27 Python
JavaScript实现一维数组转化为二维数组
2018/04/17 Python
对python-3-print重定向输出的几种方法总结
2018/05/11 Python
pycharm不能运行.py文件的解决方法
2020/02/12 Python
python中对二维列表中一维列表的调用方法
2020/06/07 Python
如何将Pycharm中调整字体大小的方式设置为"ctrl+鼠标滚轮上下滑"
2020/11/17 Python
基于Python中Remove函数的用法讨论
2020/12/11 Python
全面介绍python中很常用的单元测试框架unitest
2020/12/14 Python
scrapy实践之翻页爬取的实现
2021/01/05 Python
CSS3的Border-radius轻松制作圆角
2012/12/24 HTML / CSS
安全检查验收制度
2014/01/12 职场文书
银行职员个人的工作自我评价
2014/02/15 职场文书
物流管理专业毕业生自荐信
2014/03/04 职场文书
签约仪式主持词
2014/03/19 职场文书
课前三分钟演讲稿
2014/04/24 职场文书
市场调研项目授权委托书范本
2014/10/04 职场文书
九九重阳节标语
2014/10/07 职场文书
2015年前台接待工作总结
2015/05/04 职场文书
推普标语口号大全
2015/12/26 职场文书