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中判断一个值是否在数组中并没有直接使用
Dec 17 Javascript
jquery限定文本框只能输入数字即整数和小数
Nov 29 Javascript
node.js中的fs.readFile方法使用说明
Dec 15 Javascript
jquery实现兼容IE8的异步上传文件
Jun 15 Javascript
Node.js中流(stream)的使用方法示例
Jul 16 Javascript
基于react后端渲染模板引擎noox发布使用
Jan 11 Javascript
vue.js根据代码运行环境选择baseurl的方法
Feb 28 Javascript
Angular使用动态加载组件方法实现Dialog的示例
May 11 Javascript
详解VUE里子组件如何获取父组件动态变化的值
Dec 26 Javascript
Vue切换组件实现返回后不重置数据,保留历史设置操作
Jul 21 Javascript
微信小程序实现modal弹出框遮罩层组件(可带文本框)
Dec 20 Javascript
浅析JavaScript中的变量提升
Jun 01 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
《星际争霸II》全新指挥官斯台特曼现已上线
2020/03/08 星际争霸
基于header的一些常用指令详解
2013/06/06 PHP
php检索或者复制远程文件的方法
2015/03/13 PHP
php将图片文件转换成二进制输出的方法
2015/06/10 PHP
php验证码生成代码
2015/11/11 PHP
ThinkPHP使用getlist方法实现数据搜索功能示例
2017/05/08 PHP
PHP实现mysqli批量执行多条语句的方法示例
2017/07/22 PHP
Laravel第三方包报class not found的解决方法
2019/10/13 PHP
PHP查找一列有序数组是否包含某值的方法
2020/02/07 PHP
phpstorm激活码2020附使用详细教程
2020/09/25 PHP
jQuery 前的按键判断代码
2010/03/19 Javascript
Javascript中的几种URL编码方法比较
2015/01/23 Javascript
javascript实现一个简单的弹出窗
2016/02/22 Javascript
JavaScript 控制字体大小设置的方法
2016/11/23 Javascript
AngularJS使用ng-app自动加载bootstrap框架问题分析
2017/01/04 Javascript
vue微信分享 vue实现当前页面分享其他页面
2017/12/02 Javascript
vue3.0 CLI - 2.6 - 组件的复用入门教程
2018/09/14 Javascript
js实现的在本地预览图片功能示例
2019/11/09 Javascript
AutoJs实现刷宝短视频的思路详解
2020/05/22 Javascript
[01:17]辉夜杯战队访谈宣传片—EHOME
2015/12/25 DOTA
浅谈Python peewee 使用经验
2017/10/20 Python
python3中函数参数的四种简单用法
2018/07/09 Python
Python中asyncio模块的深入讲解
2019/06/10 Python
Python数据类型之列表和元组的方法实例详解
2019/07/08 Python
代码总结Python2 和 Python3 字符串的区别
2020/01/28 Python
Python中itertools的用法详解
2020/02/07 Python
CSS3 transforms应用于背景图像的解决方法
2019/04/16 HTML / CSS
AE美国鹰美国官方网站:American Eagle Outfitters
2016/08/22 全球购物
Quiksilver美国官网:始于1969年的优质冲浪服和滑雪板外套
2020/04/20 全球购物
介绍一下SOA和SOA的基本特征
2016/02/24 面试题
《秋游》教学反思
2014/04/24 职场文书
党员志愿者活动方案
2014/08/28 职场文书
2015元旦节寄语
2014/12/08 职场文书
学会掌握自己命运的十条黄金法则:
2019/08/08 职场文书
python实现黄金分割法的示例代码
2021/04/28 Python
详解Python为什么不用设计模式
2021/06/24 Python