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 相关文章推荐
关于__defineGetter__ 和__defineSetter__的说明
May 12 Javascript
javascritp实现input输入框相关限制用法
Jun 29 Javascript
jquery 跨域访问问题解决方法(笔记)
Jun 08 Javascript
漂亮的jquery提示效果(仿腾讯弹出层)
Feb 05 Javascript
JavaScript伸缩的菜单简单示例
Dec 03 Javascript
javascript修改图片src的方法
Jan 27 Javascript
jquery+CSS实现的多级竖向展开树形TRee菜单效果
Aug 24 Javascript
javascript中eval解析JSON字符串
Feb 27 Javascript
JS数组操作(数组增加、删除、翻转、转字符串、取索引、截取(切片)slice、剪接splice、数组合并)
May 20 Javascript
vue2.0实战之基础入门(1)
Mar 27 Javascript
JS库中的Particles.js在vue上的运用案例分析
Sep 13 Javascript
详解vue中使用vue-quill-editor富文本小结(图片上传)
Apr 24 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中导出数据到excel时数字变为科学计数的解决方法
2013/02/03 PHP
一个严格的PHP Session会话超时时间设置方法
2014/06/10 PHP
javascript多种数据类型表格排序代码分析
2010/09/11 Javascript
在vs2010中调试javascript代码方法
2011/02/11 Javascript
javascript unicode与GBK2312(中文)编码转换方法
2013/11/14 Javascript
js document.write()使用介绍
2014/02/21 Javascript
Jquery原生态实现表格header头随滚动条滚动而滚动
2014/03/18 Javascript
Jquery遍历Json数据的方法
2015/04/20 Javascript
Javascript验证Visa和MasterCard信用卡号的方法
2015/07/27 Javascript
基于JavaScript实现百叶窗动画效果不只单纯flas可以实现
2016/02/29 Javascript
javascript函数中的3个高级技巧
2016/09/22 Javascript
jQuery读取XML文件的方法示例
2017/02/03 Javascript
微信小程序遇到修改数据后页面不渲染的问题解决
2017/03/09 Javascript
浅谈SpringMVC中post checkbox 多选框value的值(隐藏域方式)
2018/01/08 Javascript
在微信小程序中渲染HTML内容的方法示例
2018/09/28 Javascript
在Layui 的表格模板中,实现layer父页面和子页面传值交互的方法
2019/09/10 Javascript
JavaScript实现网页tab栏效果制作
2020/11/20 Javascript
VUE实现吸底按钮
2021/03/04 Vue.js
[01:23]一分钟告诉你 DOTA2为什么叫信仰2
2014/06/20 DOTA
查看Django和flask版本的方法
2018/05/14 Python
python批量获取html内body内容的实例
2019/01/02 Python
Python3之手动创建迭代器的实例代码
2019/05/22 Python
使用python获取邮箱邮件的设置方法
2019/09/20 Python
pycharm部署、配置anaconda环境的教程
2020/03/24 Python
Python3实现打印任意宽度的菱形代码
2020/04/12 Python
不到20行实现Python代码即可制作精美证件照
2020/04/24 Python
Python迭代器协议及for循环工作机制详解
2020/07/14 Python
pandas处理csv文件的方法步骤
2020/10/16 Python
法国创作个性化T恤衫和其他定制产品平台:Tostadora
2018/04/08 全球购物
Sport-Thieme荷兰:购买体育用品
2019/08/25 全球购物
哈理工毕业生的求职信
2013/12/22 职场文书
公安派出所所长四风问题个人对照检查材料
2014/10/04 职场文书
明星邀请函
2015/02/02 职场文书
环境卫生标语
2015/08/03 职场文书
励志语录:时光飞逝,请学会珍惜所有的人和事
2020/01/16 职场文书
CSS font-variation 可变字体的魅力(实例详解)
2022/03/03 HTML / CSS