JavaScript 复杂判断的更优雅写法-LMLPHP

作者:Think

来源:大转转FE

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

    /**
     * 按钮点击事件
     * @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,因为我看到很多人依然在用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: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情况下,status1-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))
    }

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

    总结

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

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

    文末福利

    能看到文末我都当你是自已人了

    JavaScript 复杂判断的更优雅写法-LMLPHP

    真心感谢一路相伴,一路支持的铁汁们,爱你们〜〜

    今天带来了几本关于如何编写高质量代码的新书免费包邮送给大家

    先来一睹芳容吧,它长这样~~

    JavaScript 复杂判断的更优雅写法-LMLPHP

    简单介绍一下

    「Web前端性能优化」本次送出7本给大家

    前端性能就永远是时刻要关注的问题,阅读本书掌握前端系统、实用、专业的性能优化解决方案

    构筑前端性能知识体系,将零散知识点聚沙成塔,理清脉络

    针对6大优化场景,层层剥茧式分析,让读者知其然也知其所以然

    沉淀作者5年一线大厂开发经验,逐个性能点解析实践场景

    汇集12年专业知识,带你全面理解关乎性能的前因后果


    想要了解更多的也可以点击下方小程序

    获取姿势

    推荐阅读:

    知乎高赞:什么是前端工程化

    尤雨溪:Vue 3 计划放弃支持 IE11

    万字总结,体系化带你全面认识 Nginx !

    面试官:bind、call、apply 区别?如何实现一个bind?

    本文分享自微信公众号 - Vue中文社区(vue_fe)。
    如有侵权,请联系 [email protected] 删除。
    本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

    04-19 18:31