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入门教程(1) 什么是JS
Jan 31 Javascript
使用jQuery简化Ajax开发 Ajax开发入门
Oct 14 Javascript
在一个js文件里远程调用jquery.js会在ie8下的一个奇怪问题
Nov 28 Javascript
利用jQuery插件扩展识别浏览器内核与外壳的类型和版本的实现代码
Oct 22 Javascript
jQuery时间轴插件使用详解
Jul 16 Javascript
JavaScript多线程详解
Aug 12 Javascript
AngularJs concepts详解及示例代码
Sep 01 Javascript
JavaScript中apply方法的应用技巧小结
Sep 29 Javascript
js当前页面登录注册框,固定div,底层阴影的实例代码
Oct 04 Javascript
Vue Router的手写实现方法实现
Mar 02 Javascript
Element Input输入框的使用方法
Jul 26 Javascript
解决vue $http的get和post请求跨域问题
Jun 07 Vue.js
微信公众号网页分享功能开发的示例代码
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
DSP接收机前端设想
2021/03/02 无线电
跟我学小偷程序之成功偷取首页(第三天)
2006/10/09 PHP
PHP array操作10个小技巧分享
2011/06/23 PHP
thinkphp autoload 命名空间自定义 namespace
2015/07/17 PHP
PHP微信开发之查询微信精选文章
2016/06/23 PHP
PHP实现十进制、二进制、八进制和十六进制转换相关函数用法分析
2017/04/25 PHP
Laravel框架中VerifyCsrfToken报错问题的解决
2017/08/30 PHP
Laravel 验证码认证学习记录小结
2019/12/20 PHP
IE和Firefox的Javascript兼容性总结[推荐收藏]
2011/10/19 Javascript
js中根据字数截取字符串,不能截断url
2012/01/12 Javascript
Javascript调用函数方法的几种方式介绍
2015/03/20 Javascript
jQuery Validation PlugIn的使用方法详解
2015/12/18 Javascript
浅谈JavaScript 中有关时间对象的方法
2016/08/15 Javascript
JavaScript实现的微信二维码图片生成器的示例
2016/10/26 Javascript
无限循环轮播图之运动框架(原生JS实现)
2017/10/01 Javascript
微信小程序实现获取自己所处位置的经纬度坐标功能示例
2017/11/30 Javascript
angular将html代码输出为内容的实例
2018/09/30 Javascript
微信小程序时间选择插件使用详解
2018/12/28 Javascript
JS原生瀑布流效果实现
2019/04/26 Javascript
JavaScript实现多张图片放大镜效果示例【不限定图片尺寸,rem单位】
2019/05/14 Javascript
浅入深出Vue之组件使用
2019/07/11 Javascript
JS简易计算器实例讲解
2020/06/30 Javascript
[02:51]2014DOTA2 TI小组赛总结中国军团全部进军钥匙球馆
2014/07/15 DOTA
python pickle 和 shelve模块的用法
2013/09/16 Python
编写Python脚本批量下载DesktopNexus壁纸的教程
2015/05/06 Python
Python列表list操作相关知识小结
2020/01/29 Python
Python求解排列中的逆序数个数实例
2020/05/03 Python
VSCode中autopep8无法运行问题解决方案(提示Error: Command failed,usage)
2021/03/02 Python
澳洲最大的时尚奢侈品电商平台:Cettire
2020/06/15 全球购物
2014年预备党员学习两会心得体会
2014/03/17 职场文书
二手房购房意向书范本
2014/04/01 职场文书
《长相思》听课反思
2014/04/10 职场文书
职工年度考核评语
2014/12/31 职场文书
欠款证明
2015/06/24 职场文书
2016学习医德医风心得体会
2016/01/25 职场文书
导游词之平津战役纪念馆
2019/11/04 职场文书