JS复杂判断的更优雅写法代码详解

作者:半夏

时间:2026-10-23

浏览:332

本文深入探讨JavaScript中复杂条件判断的优化方案,对比if/else、switch、对象映射及ES6 Map的使用场景。通过从一元到多元、再到正则匹配的案例演进,展示如何利用Map和函数缓存提升代码可读性与执行效率,解决嵌套逻辑臃肿问题。

文章目录

复杂判断的挑战与优化思路

在JavaScript开发中,处理复杂逻辑判断是常见需求。通常开发者会使用if/else或switch语句来实现多条件分支,但随着业务逻辑复杂度的提升,这类代码往往变得冗长且难以维护。本文将介绍几种更优雅的判断逻辑实现方式,帮助提升代码质量。

接下来通过具体示例进行说明。

首先观察以下基础代码片段。


/**
 * 按钮点击事件
 * @param {number} status 活动状态:1 开团进行中 2 开团失败 3 商品售罄 4 开团成功 5 系统取消
 */
const onButtonClick1 = (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与对象映射

上述代码展示了按钮点击事件的逻辑:根据活动状态执行日志埋点发送及页面跳转操作。面对这种多分支场景,常见的改写思路是使用switch语句。


/**
 * 按钮点击事件
 * @param {number} status 活动状态:1 开团进行中 2 开团失败 3 商品售罄 4 开团成功 5 系统取消
 */
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
 }
}

使用switch后,代码结构相比if/else更为清晰。此外,这里展示了一个技巧:当case 2和case 3的处理逻辑相同时,可以省略case 2中的执行语句和break关键字,使其直接落入case 3的逻辑中执行。

部分开发者可能会提出更简洁的替代方案。


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

进阶方案:ES6 Map对象的应用

此处引入了ES6中的Map对象,相比传统对象,它提供了更强大的功能。那么Map与Object的主要区别在哪里?

首先,普通对象通常包含原型链上的prototype属性,而Map没有。其次,对象的键必须是字符串或Symbol类型,而Map的键可以是任意数据类型。

此外,Map提供了size属性,可以方便地获取键值对的数量;而获取普通对象的键值对数量则需要手动计算或借助Object.keys()等方法。

多元条件判断:二元与三元逻辑处理

现在我们将场景升级:按钮点击事件不仅需要判断status,还需要结合用户身份(identity)进行综合判断。


/**
 * 按钮点击事件
 * @param {number} status 活动状态:1开团进行中 2开团失败 3 开团成功 4 商品售罄 5 有库存未开团
 * @param {string} identity 身份标识:guest客态 master主态
 */
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
  }
 }
}

此处省略具体的业务逻辑实现,因为代码量较大。

这里再次使用了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对象作为Map的Key。


const actions = new Map([
 [{identity:'guest',status:1},()=>{/*do sth*/}],
 [{identity:'guest',status:2},()=>{/*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身份下,status 1至4的处理逻辑完全相同,最直观但低效的写法如下。


const actions = 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 actions = ()=>{
 const functionA = ()=>{/*do sth*/}
 const functionB = ()=>{/*do sth*/}
 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))
}

这种写法已能满足日常需求,但若仔细审视,上述代码中重复定义了4次functionA,这并不理想。假如判断条件更加复杂,例如identity有3种状态,status有10种状态,则需要定义30条处理逻辑。由于大量逻辑可能相同,这种重复令人难以接受。此时可以优化为:


const actions = ()=>{
 const functionA = ()=>{/*do sth*/}
 const functionB = ()=>{/*do sth*/}
 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的优势更加凸显,因为可以使用正则表达式作为Key,从而带来无限可能性。例如,需求变为:所有guest身份均需发送日志埋点,而不同status需执行独立逻辑,实现方式如下:


const actions = ()=>{
 const functionA = ()=>{/*do sth*/}
 const functionB = ()=>{/*do sth*/}
 const functionC = ()=>{/*send log*/}
 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))
}

利用数组循环特性,符合正则条件的逻辑将被依次执行。这样既能执行公共逻辑,又能处理特定逻辑。借助正则表达式的灵活性,可以解锁更多高级玩法,本文不再赘述。

总结

以上介绍了JavaScript中复杂判断的多种优雅写法,希望对读者有所帮助。如有任何疑问,欢迎留言讨论,小编会及时回复。感谢大家对本站的支持!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章