我们编写js代码时经常遇到复杂逻辑判断的情况,通常大家可以用if/else或者switch来实现多个条件判断,但这样会有个问题,随着逻辑复杂度的增加,代码中的if/else/switch会变得越来越臃肿,越来越看不懂,那么如何更优雅的写判断逻辑,本文带你试一下。

举个例子

先看一段代码

/**
* 按钮点击事件
* @param {number} status 活动状态:1 开团进行中 2 开团失败 3 商品售罄 4 开团成功 5 系统取消
*/
const onButtonClick = (status) => {
if (status == ) {
sendLog('processing') jumpTo('IndexPage')
} else if (status == ) {
sendLog('fail') jumpTo('FailPage')
} else if (status == ) {
sendLog('fail') jumpTo('FailPage')
} else if (status == ) {
sendLog('success') jumpTo('SuccessPage')
} else if (status == ) {
sendLog('cancel') jumpTo('CancelPage')
} else {
sendLog('other') jumpTo('Index')
}
}

通过代码可以看到这个按钮的点击逻辑:根据不同活动状态做两件事情,发送日志埋点和跳转到对应页面,大家可以很轻易的提出这段代码的改写方案,switch出场:

/**
* 按钮点击事件
* @param {number} status 活动状态:1 开团进行中 2 开团失败 3 商品售罄 4 开团成功 5 系统取消
*/
const onButtonClick = (status) => {
switch (status) {
case :
sendLog('processing')
jumpTo('IndexPage')
break
case :
case :
sendLog('fail')
jumpTo('FailPage')
break
case :
sendLog('success')
jumpTo('SuccessPage')
break
case :
sendLog('cancel')
jumpTo('CancelPage')
break
default:
sendLog('other')
jumpTo('Index')
break
}
}

嗯,这样看起来比if/else清晰多了,细心的同学也发现了小技巧,case 2和case 3逻辑一样的时候,可以省去执行语句和break,则case 2的情况自动执行case 3的逻辑。

这时有同学会说,还有更简单的写法:

const actions = {
'': ['processing', 'IndexPage'],
'': ['fail', 'FailPage'],
'': ['fail', 'FailPage'],
'': ['success', 'SuccessPage'],
'': ['cancel', 'CancelPage'],
'default': ['other', 'Index'],
}
/**
* 按钮点击事件
* @param {number} status 活动状态:1开团进行中 2开团失败 3 商品售罄 4 开团成功 5 系统取消
*/
const onButtonClick = (status) => {
let action = actions[status] || actions['default'],
logName = action[],
pageName = action[]
sendLog(logName)
jumpTo(pageName)
}

上面代码确实看起来更清爽了,这种方法的聪明之处在于:将判断条件作为对象的属性名,将处理逻辑作为对象的属性值,在按钮点击的时候,通过对象属性查找的方式来进行逻辑判断,这种写法特别适合一元条件判断的情况。

是不是还有其他写法呢?有的:

const actions = new Map([
[, ['processing', 'IndexPage']],
[, ['fail', 'FailPage']],
[, ['fail', 'FailPage']],
[, ['success', 'SuccessPage']],
[, ['cancel', 'CancelPage']],
['default', ['other', 'Index']]
])
/**
* 按钮点击事件
* @param {number} status 活动状态:1 开团进行中 2 开团失败 3 商品售罄 4 开团成功 5 系统取消
*/
const onButtonClick = (status) => {
let action = actions.get(status) || actions.get('default')
sendLog(action[])
jumpTo(action[])
}

这样写用到了es6里的Map对象,是不是更爽了?Map对象和Object对象有什么区别呢?

  1. 一个对象通常都有自己的原型,所以一个对象总有一个"prototype"键。

  2. 一个对象的键只能是字符串或者Symbols,但一个Map的键可以是任意值。

  3. 你可以通过size属性很容易地得到一个Map的键值对个数,而对象的键值对个数只能手动确认。

我们需要把问题升级一下,以前按钮点击时候只需要判断status,现在还需要判断用户的身份:

/**
* 按钮点击事件
* @param {number} status 活动状态:1开团进行中 2开团失败 3 开团成功 4 商品售罄 5 有库存未开团
* @param {string} identity 身份标识:guest客态 master主态
*/
const onButtonClick = (status, identity) => {
if (identity == 'guest') {
if (status == ) {
//do sth
} else if (status == ) {
//do sth
} else if (status == ) {
//do sth
} else if (status == ) {
//do sth
} else if (status == ) {
//do sth
} else {
//do sth
}
} else if (identity == 'master') {
if (status == ) {
//do sth
} else if (status == ) {
//do sth
} else if (status == ) {
//do sth
} else if (status == ) {
//do sth
} else if (status == ) {
//do sth
} else {
//do sth
}
}
}

原谅我不写每个判断里的具体逻辑了,因为代码太冗长了。

原谅我又用了if/else,因为我看到很多人依然在用if/else写这种大段的逻辑判断。

从上面的例子我们可以看到,当你的逻辑升级为二元判断时,你的判断量会加倍,你的代码量也会加倍,这时怎么写更清爽呢?

const actions = new Map([
['guest_1', () => { /*do sth*/ }],
['guest_2', () => { /*do sth*/ }],
['guest_3', () => { /*do sth*/ }],
['guest_4', () => { /*do sth*/ }],
['guest_5', () => { /*do sth*/ }],
['master_1', () => { /*do sth*/ }],
['master_2', () => { /*do sth*/ }],
['master_3', () => { /*do sth*/ }],
['master_4', () => { /*do sth*/ }],
['master_5', () => { /*do sth*/ }],
['default', () => { /*do sth*/ }],
])
/**
* 按钮点击事件
* @param {string} identity 身份标识:guest客态 master主态
* @param {number} status 活动状态:1 开团进行中 2 开团失败 3 开团成功 4 商品售罄 5 有库存未开团
*/
const onButtonClick = (identity, status) => {
let action = actions.get(`${identity}_${status}`) || actions.get('default')
action.call(this)
}

上述代码核心逻辑是:把两个条件拼接成字符串,并通过以条件拼接字符串作为键,以处理函数作为值的Map对象进行查找并执行,这种写法在多元条件判断时候尤其好用。

当然上述代码如果用Object对象来实现也是类似的:

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

如果有些同学觉得把查询条件拼成字符串有点别扭,那还有一种方案,就是用Map对象,以Object对象作为key:

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

是不是又高级了一点点?

这里也看出来Map与Object的区别,Map可以用任何类型的数据作为key。

我们现在再将难度升级一点点,假如guest情况下,status1-4的处理逻辑都一样怎么办,最差的情况是这样:

const actions = new Map([
[{
identity: 'guest',
status:
}, () => { /* functionA */ }],
[{
identity: 'guest',
status:
}, () => { /* functionA */ }],
[{
identity: 'guest',
status:
}, () => { /* functionA */ }],
[{
identity: 'guest',
status:
}, () => { /* functionA */ }],
[{
identity: 'guest',
status:
}, () => { /* functionB */ }],
//...
])

好一点的写法是将处理逻辑函数进行缓存:

const actions = () => {
const functionA = () => { /*do sth*/ }
const functionB = () => { /*do sth*/ }
return new Map([
[{
identity: 'guest',
status:
}, functionA],
[{
identity: 'guest',
status:
}, functionA],
[{
identity: 'guest',
status:
}, functionA],
[{
identity: 'guest',
status:
}, functionA],
[{
identity: 'guest',
status:
}, functionB],
//...
])
}
const onButtonClick = (identity, status) => {
let action = [...actions()].filter(([key, value]) => (key.identity == identity && key.status == status))
action.forEach(([key, value]) => value.call(this))
}

这样写已经能满足日常需求了,但认真一点讲,上面重写了4次functionA还是有点不爽,假如判断条件变得特别复杂,比如identity有3种状态,status有10种状态,那你需要定义30条处理逻辑,而往往这些逻辑里面很多都是相同的,这似乎也是笔者不想接受的,那可以这样实现:

const actions = () => {
const functionA = () => { /*do sth*/ }
const functionB = () => { /*do sth*/ }
return new Map([
[/^guest_[-]$/, 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的优势更加凸显,可以用正则类型作为key了,这样就有了无限可能,假如需求变成,凡是guest情况都要发送一个日志埋点,不同status情况也需要单独的逻辑处理,那我们可以这样写:

const actions = () => {
const functionA = () => { /*do sth*/ }
const functionB = () => { /*do sth*/ }
const functionC = () => { /*send log*/ }
return new Map([
[/^guest_[-]$/, 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))
}

也就是说利用数组循环的特性,符合正则条件的逻辑都会被执行,那就可以同时执行公共逻辑和单独逻辑,因为正则的存在,你可以打开想象力解锁更多的玩法,本文就不赘述了。

总结

本文已经教你了8种逻辑判断写法,包括:

  1. if/else

  2. switch

  3. 一元判断时:存到Object里

  4. 一元判断时:存到Map里

  5. 多元判断时:将condition拼接成字符串存到Object里

  6. 多元判断时:将condition拼接成字符串存到Map里

  7. 多元判断时:将condition存为Object存到Map里

  8. 多元判断时:将condition写作正则存到Map里

至此,本文也将告一段落,愿你未来的人生里,不只是有if/else/switch。

JavaScript 复杂判断的更优雅写法的更多相关文章

  1. JavaScript复杂判断的更优雅写法

    摘要: 写代码是一门艺术. 原文:JavaScript 复杂判断的更优雅写法 作者:Think. 公众号:大转转fe Fundebug经授权转载,版权归原作者所有. 前提 我们编写js代码时经常遇到复 ...

  2. 【转】转自微信公众号 JavaScript 复杂判断的更优雅写法

    与微信公众号看到一篇js复杂判断的文章,对我启发很大,故转到博客园以供后期不断学习并应用于项目.原文地址:https://mp.weixin.qq.com/s/ClFDRj4MnAxv1dJ5VWKS ...

  3. JavaScript 复杂判断的更优雅写法借鉴

    前言: 我们编写js代码时经常遇到复杂逻辑判断的情况,通常大家可以用if/else或者switch来实现多个条件判断,但这样会有个问题,随着逻辑复杂度的增加,代码中的if/else/switch会变得 ...

  4. JavaScript 复杂判断的优雅写法

    JavaScript 复杂判断的优雅写法 <div> <input type="button" name="btn" value=" ...

  5. C#中一种替换switch语句更优雅的写法

    今天在项目中遇到了使用switch语句判断条件,但问题是条件比较多,大概有几十个条件,满屏幕的case判断,是否有更优雅的写法替代switch语句呢? 假设有这样的一个场景:商场经常会根据情况采取不同 ...

  6. 使用 Promises 编写更优雅的 JavaScript 代码

    你可能已经无意中听说过 Promises,很多人都在讨论它,使用它,但你不知道为什么它们如此特别.难道你不能使用回调么?有什么了特别的?在本文中,我们一起来看看 Promises 是什么以及如何使用它 ...

  7. es6之更优雅的条件语句

    在使用JavaScript时,条件判断是经常会用到的,一些简单的判断条件还可以接受,当遇到比较复杂多重条件时就比较恶心了.这里使用es6的小技巧使判断更优雅. 1.使用 Arrary.includes ...

  8. jquery.form.js 让表单提交更优雅

    jquery.form.js 让表单提交更优雅.可以页面不刷新提交表单,比jQuery的ajax提交要功能强大. 1.引入 <script src="/src/jquery-1.9.1 ...

  9. JavaScript中判断变量类型最简洁的实现方法以及自动类型转换(#################################)

    这篇文章主要介绍了JavaScript中判断整字类型最简洁的实现方法,本文给出多个判断整数的方法,最后总结出一个最短.最简洁的实现方法,需要的朋友可以参考下 我们知道JavaScript提供了type ...

随机推荐

  1. docker容器的操作

    容器操作 1.查看本地容器 docker ps -a # 查看所有容器 docker ps # 查看所有正在运行的容器 2.容器的创建与启动 方法一:两步完成(强烈推荐方法)(1)创建 docker ...

  2. (Detected problems with API compatibility(visit g.co/dev/appcompat for more info)

    在applicaiton里面加载这么一段代码: private void closeAndroidPDialog(){ try { Class aClass = Class.forName(" ...

  3. Python3学习之路~5.10 PyYAML模块

    Python也可以很容易的处理ymal文档格式,只不过需要安装一个模块,参考文档:http://pyyaml.org/wiki/PyYAMLDocumentation

  4. node (02 CommonJs 和 Nodejs 中自定义模块)顺便讲讲module.exports和exports的区别 dependencies 与 devDependencies 之间的区别

    CommonJS 规范的提出,主要是为了弥补当前 JavaScript 没有标准的缺陷.它的终极目标就是:提供一个类似 Python,Ruby 和 Java 语言的标准库,而不只是停留在小脚本程序的阶 ...

  5. windows安装composer总结

    1.直接去网吧下载windows安装EXE程序,傻瓜式安装,so easy. 2.通过命令行安装,可以直接在php目录跑起来 php -r "readfile('https://getcom ...

  6. javax.el.PropertyNotFoundException: Property 'know_id' not found on type java.lang.String

    今天通过Servlet明明查出来了结果,在跳转到页面时报这个异常.根据经验仔细核对了字段书写时,未发现错误. 耐心仔细检查之后发现el表达式的List集合写错了 <c:forEach items ...

  7. Html fieldset、legend 标签

    Html fieldset.legend 标签 <html> <body> <!-- fieldset 添加圈起标题框标签 --> <fieldset> ...

  8. 【题解】JSOIWC2019 Round 5

    题面: 题解: T1: 算法1: 枚举每个灯塔的方向,并分别判断是否有解.时间复杂度O(K*4^K). 预计得分:50-70分 算法2: 不难发现,当k≥4的时候一定有解,将最靠左的两个下面的朝右上. ...

  9. springboot项目打成war包

    在某种情况下,比如..........之下,我们不得不,将springboot打成war包 1.在pom.xml文件中修改 <packaging>war</packaging> ...

  10. JS设计模式(8)模板方法模式

    什么是模板方法模式? 定义:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中.模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤. 主要解决:一些方法通用,却在每一个子类都重新 ...