关键词:
原文:写 JS 逻辑判断,不要只知道用 if-else 和 switch
我们在编写 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 逻辑判断的写法,包括:
- if/else
- switch
- 单一判断:存储在 Object 中
- 单一判断:存储在 Map 对象中
- 多重判断:将条件串联成一个字符串,存储在 Object 中
- 多重判断:将条件连成一个字符串,存储在 Map 对象中
- 多重判断:把条件作为对象存储在 Map 中
- 多重判断:把条件写成正则式存储在 Map 中
今天就分享到这里,愿你今后的编码生活不仅仅只有 if/else 或 switch。
写if-else不外乎两种场景:异常逻辑处理和不同状态处理。
写if-else不外乎两种场景:异常逻辑处理和不同状态处理。异常逻辑处理说明只能一个分支是正常流程,而不同状态处理都所有分支都是正常流程。第一个例子`if(obj!=null)`是异常处理,是代码健壮性判断,只有if里面才是正常的处理流... 查看详情
有啥完美的方法替代java中的if-else,switch-case
如果有大量同样的逻辑分支判断组,只是执行内容不一样,那么经典方法是用多态,建一个抽象基类,里面包含一个抽象函数。每个子类代表一个逻辑分支,实现抽象函数时,函数逻辑对应之前的if分支的内容。但是一定会保留... 查看详情
零散记录
...沉淀到自己手里头 把变化收敛在简单的地方,复杂的逻辑里不要加一堆if-else(比如for里面尽量少一大堆if-else)要收口,不要遍地开花技术手段,心理手段用别人的方法一定要关注类型有耐心,注意总 查看详情
零基础学c语言知识总结四:if-else判断语句和switch-case选择语句(代码片段)
...关键字if开头,跟上在括号()里边的是表示逻辑条件的表达式,然后是一对大括号中间是若干条语句,如果条件的逻辑表达式的结果不是零,那么就执行大括号中间的语句,否则就跳过不执行。if的返... 查看详情
求求你们了,别再写满屏的if/else了!(代码片段)
...想必都经历过这样的场景:刚开始自己写的代码很简洁,逻辑清晰,函数精简,没有一个if-else,可随着代码逻辑不断完善和业务的瞬息万变:比如需要对入参进行类型和值进行判断;这里要判断下对象是否为null;不同类型执行不... 查看详情
有啥完美的方法替代java中的if-else,switch-case
如果有大量同样的逻辑分支判断组,只是执行内容不一样,那么经典方法是用多态,建一个抽象基类,里面包含一个抽象函数。每个子类代表一个逻辑分支,实现抽象函数时,函数逻辑对应之前的if分支的内容。但是一定会保留... 查看详情
优雅的替换if-else语句
日常开发,if-else语句写的不少吧??当逻辑分支非常多的时候,if-else套了一层又一层,虽然业务功能倒是实现了,但是看起来是真的很不优雅,尤其是对于我这种有强迫症的程序"猿",看到这么多if-else,脑袋瓜子就嗡嗡的,总... 查看详情
else if 怎么写成逻辑语句?
】elseif怎么写成逻辑语句?【英文标题】:Howtowriteelseifaslogicalstatement?【发布时间】:2022-01-0712:57:13【问题描述】:我想写一个if-else语句作为逻辑语句。我知道:if(statement1)b=celseb=d可以写成:b=(statement1&&c)||(!statement1&&... 查看详情
我要快乐的度过我的实习期
...的一个月里,自己独立的完成了一个页面。刚开始的时候逻辑不怎么复杂,写起来还算得心应手,说的有点夸张了。基本知道用什么,就是记得不是太清,要去百度,要看参考手册。代码不足之处:1.动态创建元素熟悉。2.一开... 查看详情
写规范代码的意义------去除魔法数字
...拿到需求,看看需求有什么漏洞,然后罗列出需求的业务逻辑。实现常用功能基本没有技术障碍了。也知道写个好代码是那么难,那么有意义。很经常看到这样的代码.Stringname="xiaoming";if(name.eq 查看详情
怎么用cookie控制一个ip只执行一次js代码
不要完全照搬网上搜索来的,照搬请带详细说明,因为网页搜索出来的我搞不明白,我自己也搜索过的。谢谢了。cookie控制一个IP24小时只执行一次JS退出弹窗代码最好是JS文件代码(就是写在JS文件里面的外码)可以过绝大部分... 查看详情
如何优雅的用策略模式,取代臃肿的if-else嵌套,看这篇就够了(代码片段)
...公司推广入口很多,每一个下单来源在下单时都做特殊的逻辑处理,可能每两天就会加一个来源一、传统的实现方式那么按照传统的实现方式代码就是如下:publicclassOrderServiceImplimplementsIOrderService@OverridepublicStringhandle(OrderDTOdto)Str... 查看详情
if循环判断(代码片段)
if循环判断if-else循环的语法格式if逻辑判断句:?代码块#缩进表示所属关系else逻辑判断句:?代码块if和elif同时使用来做多层判断if逻辑判断式:代码块elif逻辑判断式:?代码块if的嵌套使用在if判断句中可以继续加入if的判断模块... 查看详情
小白写了一堆if-else,大神实在看不下去了,竟然用策略模式直接摆平了(代码片段)
...着如下几个问题来阅读本文:如何通过策略模式优化业务逻辑代码(可以根据自己从事的工作思考)使用策略模式优化if-else,会不会带来什么副作用呢?实现策略模式是否有更好的方式呢?1.策略模式如何优化代码解构要会带这... 查看详情
消除代码中的if-else/switch-case
...码中会有很多分支,而且分支下面的代码又有一些复杂的逻辑,相信很多人都喜欢用if-else/switch-case去实现。做的不好的会直接把实现的代码放在if-else/switch-case的分支之下:switch(type)casecase1:......break;casecase2:......break;casecase3:......b... 查看详情
缩短 Ruby 中的 if-else 结构
...经写过类似的东西,与我从VisualBasic6.0中知道的相同if-else逻辑,但我确信有更好的“Ruby方式”来编写它。你能告诉我它在Ruby世界中的样子吗?ifparams[:medication_name].nil?med_name=\'all 查看详情
约束逻辑程序中的奇怪警告和计算结果
】约束逻辑程序中的奇怪警告和计算结果【英文标题】:Strangewarningandcomputationresultinconstraintlogicprogram【发布时间】:2015-01-1312:35:39【问题描述】:首先,很抱歉发布整个程序,但我不知道问题是我不知道哪些部分是不相关的。... 查看详情
C# 数字和字母
...母或除数字以外的任何内容执行操作。不要这样做。如何判断字符串是字母还是数字?很简单,但我不会写这段代码。【问题讨论】:请说得更具体一些,你想知道它是只包含字母还是只包含数字?@atticae-很难说,但在我们的答... 查看详情