我们在编写 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 中
RTX 5090要首发 性能要翻倍!三星展示GDDR7显存
三星在GTC上展示了专为下一代游戏GPU设计的GDDR7内存。
首次推出的GDDR7内存模块密度为16GB,每个模块容量为2GB。其速度预设为32 Gbps(PAM3),但也可以降至28 Gbps,以提高产量和初始阶段的整体性能和成本效益。
据三星表示,GDDR7内存的能效将提高20%,同时工作电压仅为1.1V,低于标准的1.2V。通过采用更新的封装材料和优化的电路设计,使得在高速运行时的发热量降低,GDDR7的热阻比GDDR6降低了70%。
更新日志
- 第五街的士高《印度激情版》3CD [WAV+CUE][2.4G]
- 三国志8重制版哪个武将智力高 三国志8重制版智力武将排行一览
- 三国志8重制版哪个武将好 三国志8重制版武将排行一览
- 三国志8重制版武将图像怎么保存 三国志8重制版武将图像设置方法
- 何方.1990-我不是那种人【林杰唱片】【WAV+CUE】
- 张惠妹.1999-妹力新世纪2CD【丰华】【WAV+CUE】
- 邓丽欣.2006-FANTASY【金牌大风】【WAV+CUE】
- 饭制《黑神话》蜘蛛四妹手办
- 《燕云十六声》回应跑路:年内公测版本完成95%
- 网友发现国内版《双城之战》第二季有删减:亲亲环节没了!
- 邓丽君2024-《漫步人生路》头版限量编号MQA-UHQCD[WAV+CUE]
- SergeProkofievplaysProkofiev[Dutton][FLAC+CUE]
- 永恒英文金曲精选4《TheBestOfEverlastingFavouritesVol.4》[WAV+CUE]
- 群星《国风超有戏 第9期》[320K/MP3][13.63MB]
- 群星《国风超有戏 第9期》[FLAC/分轨][72.56MB]