如何优化JavaScript中的复杂判断?

开发 前端
我们在写JavaScript代码的时候,经常会遇到逻辑判断比较复杂的情况,通常我们可以使用if/else或者switch来实现多个条件判断。

我们在写JavaScript代码的时候,经常会遇到逻辑判断比较复杂的情况,通常我们可以使用if/else或者switch来实现多个条件判断。

但是有一个问题,随着逻辑复杂度的增加,代码中的if/else/switch会越来越臃肿,难以理解。那么如何才能写出更优雅的判断逻辑呢?

示例:

/**
 * Button click event
 * @param {number} status:1 2 3 4 5
 */
const onButtonClick = (status)=>{
  if(status == 1){
    jumpTo('IndexPage')
  }else if(status == 2){
    jumpTo('FailPage')
  }else if(status == 3){
    jumpTo('FailPage')
  }else if(status == 4){
    jumpTo('SuccessPage')
  }else if(status == 5){
    jumpTo('CancelPage')
  }else {
    jumpTo('Index')
  }
}

从代码中我们可以看到这个按钮的点击逻辑:根据不同的活动状态,做两件事,发送日志事件,跳转到对应页面。大家很容易就提出使用 switch 进行代码重写方案:

const onButtonClick = (status)=>{
  switch (status){
    case 1:
      console.log('IndexPage')
      break
    case 2:
    case 3:
      jumpTo('FailPage')
      break  
    case 4:
      jumpTo('SuccessPage')
      break
    case 5:
      jumpTo('CancelPage')
      break
    default:
      jumpTo('Index')
      break
  }
}

这样看起来比用 if/else 清晰多了,你还发现了一个小技巧:当 case 2 和 case 3 的逻辑相同时,可以省略执行语句和 break,这样 case 2 的逻辑就会自动执行 case 3 的逻辑。

持续优化:

const actions = {
  '1': ['IndexPage'],
  '2': ['FailPage'],
  '3': ['FailPage'],
  '4': ['SuccessPage'],
  '5': ['CancelPage'],
  'default': ['Index'],
}
const onButtonClick = (status)=>{
  let action = actions[status] || actions['default'],
  jumpTo(action[0])
}

现在代码确实看起来干净多了,这个方法的巧妙之处在于:把判定条件作为对象的属性名,把处理逻辑作为对象的属性值,点击按钮时通过查找对象属性进行逻辑判断,这种写法特别适合一元条件判断。

还有其他写法吗?用map:

const actions = new Map([
  [1, ['IndexPage']],
  [2, ['FailPage']],
  [3, ['FailPage']],
  [4, ['SuccessPage']],
  [5, ['CancelPage']],
  ['default', ['Index']]
])
const onButtonClick = (status)=>{
  let action = actions.get(status) || actions.get('default')
  jumpTo(action[0])
}

这样写的话,就用到了ES6中的Map对象,是不是感觉顺畅多了?Map对象和Object有什么区别?

  • 对象通常有自己的原型,所以对象总有一个原型键。
  • 对象的键只能是字符串或者Symbol,而Map的键可以是任意值。

Map中键值对的数量可以通过size属性轻松获取,而对象中键值对的数量只能手动确认。

复杂一点的话,再加一层判断如何?

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', ()=>{/*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*/}],
])
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)
}

如果把查询条件拼接成字符串感觉有点别扭的话,还有另外一个解决办法,就是使用一个以 Object 对象为键的 Map 对象:

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。

我们再增加一点难度,假设一个客人的情况,状态1-4的处理逻辑都一样,我们该如何处理呢?最坏的情况是这样的:

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))
}

这样写已经可以满足日常需求了。

不过说真的,重写 functionA 四次还是有点麻烦的。如果情况变得特别复杂,比如,身份有三种状态,状态有十种状态,那么就需要定义 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 的优势就更加凸显了,它允许使用正则类型作为键,这带来了无限可能。假设需求发生了变化,每个客人场景都需要发送日志事件,不同的状态场景也需要单独的逻辑处理。那么,我们可以这样写:

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))
}

也就是说,利用数组循环的特性,满足正则表达式条件的逻辑就会被执行,这样就可以同时执行通用逻辑和个别逻辑。有了正则表达式的存在,你可以发挥你的想象力,解锁更多的可能性。

责任编辑:华轩 来源: web前端开发
相关推荐

2024-10-07 12:23:03

字符串Map对象

2020-10-22 14:00:31

JavaScript数字变量

2020-10-22 08:06:05

JavaScrip语言类型

2009-06-11 17:15:23

JavaScript性

2009-06-10 22:00:57

JavaScript脚

2022-09-12 23:53:53

JavaScript条件判断开发

2016-08-03 17:23:47

javascripthtml前端

2011-05-27 16:00:10

DB2

2022-09-04 15:40:39

JavaScrip状态模式软件

2013-08-01 13:18:41

代码

2011-05-25 10:46:39

Javascript

2024-06-14 08:54:54

2020-08-23 11:32:21

JavaScript开发技术

2011-07-13 09:46:23

javaScript

2012-03-12 09:33:04

JavaScript

2020-12-17 07:52:38

JavaScript

2020-11-27 10:25:36

物联网设备软件

2011-06-20 14:59:33

网站优化

2022-05-18 08:00:00

JavaScriptFetch数据

2022-05-26 09:51:50

JavaScrip内存泄漏
点赞
收藏

51CTO技术栈公众号