该不应扼杀过多的if-else

面对过多的if-else,代码可能看起来比较冗余,搞很差又是一张被人处处转发的“咱们项目几百几千行if”的图。可是通过各类设计模式和封装,if大大减小,但可读性可能稍微下降了,并且比较抽象。那咱们应该如何取舍呢前端

抛开其余因素,若是if-else过多,可读性也许会好也可能会下降,可维护性也是或高或低;若是if-else少,代码高度抽象,可读性会低或者不变,可维护性可能会高也可能会低。这里大概可能会有几种状况设计模式

if平铺条件单一

这种状况,if精简不精简,可读性是不会变的,可是精简程度和可维护性是正相关的。至于为何,看一下代码就能够感觉到了模块化

执行语句单一

if (a === 1) {
    console.log('this is 1')
} else if  (a === 2) {
    console.log('this is 2')
} else if  (a === 3) {
    console.log('this is 3')
} 
// ...还有不少
else {
    console.log('this is other')
}
复制代码

精简代码:函数

// 若是上面的if a是从1到10
console.log(`this is ${a > 0 && a < 10 ? a : 'other'}`)
// 若是上面的if a是从1到5和从7-10
if ((a > 0 && a < 5) || (a > 7 && a < 10) {
    console.log(`this is ${a}`)
} else {
    console.log('this is other')
}
// a取值灵活区间
const area = [[1,2], [5,7], [10, 11]]
console.log(`${area.find(([from, to]) => { return from <= a && a <= to }) ? a : 'other'}`)
复制代码

这种状况,有没有精简,可读性都没有发生变化,若是是未精简的,写一堆if,你仍是很容易看得出干啥。而可维护性就不同了,要加一个或者多个数字,那么就要深刻到某个if分支,一个个动手改,维护性低。可是,若是精简了的话,维护性大大增长,代码也简短。只须要寻找if的规律并封装全部的case便可,最后作到“条件驱动”学习

一些极简状况

有一些很是简单的状况,可使用&&||、三元解决优化

// before
if (cb) {
    cb()
}
//after
cb && cb()

// before
if (!obj) {
    obj = {}
}
obj.a = 1
//after
(obj || obj = {}).a = 1

// before
if (type === true) {
  value = 1
} else {
  value = 2
}
//after
value = type ? 1 : 2

// before
if (type === DEL) {
  this.delateData(id)
} else {
  this.addData(id)
}
// after
this[type === DEL ? 'delateData' : 'addData'](id)
// or
;(type === DEL ? this.delateData : this.addData)(id)

// before
if (a === 1 && a === 2 && a === 10) {
    console.log('ok')
}
// after
if ([1, 2, 10].includes(a)) {
    console.log('ok')
}
复制代码

条件单1、执行语句单一的状况,建议优化指数:★★★★★ui

执行语句复杂

if (a === 1) {
    console.log('this is 1')
} else if  (a === 2) {
    console.log('this is 二')
} else if  (a === 3) {
    console.log('this is three')
} 
// ...还有不少
else {
    console.log('this is other')
}
复制代码

精简代码:this

const map = {
    1: 'this is 1',
    2: 'this is 二',
    3: 'this is three',
    // ...不少
}
console.log(map[a] || 'this is other')
复制代码

这种状况,和执行语句单一相似,也是可读性不变,代码减小了可维护性只是略好一点。一般的解决办法就是k-v映射了。加一个条件,就在map中加多一对k-v(因为条件处理复杂,因此条件上没有优化空间了,必须写出来)spa

这种场景,平时应该会比较常见转为switch。若是执行语句很复杂无规律,写k-v的缺陷就来了:一个key被迫对应一个callback函数,还会花时间斟酌传值问题,并且代码量也没发生变化,此时不建议优化设计

if (a === 1) {
    console.log('this is 1')
    alert(a * 100);
} else if  (a === 2) {
    console.log('this is 二')
    document.body.innerHTML = a + 1 + b
}
// after
const map = {
    1: (a) => {
        console.log('this is 1')
        alert(a * 100);
    },
    2: (a, b) => {
        console.log('this is 二')
        document.body.innerHTML = a + 1 + b
    }
}
map[a](a, b) // 代码量并无减小也没有加强维护性
复制代码

问题来了,条件单一,但处理语句有简单的也有复杂的怎么办?case by case,先归类再分状况,最终只会剩下少许if和switch的

小结: 条件单1、执行语句复杂的状况,有规律时建议优化指数:★★★,无规律时,建议指数:★

if平铺条件复杂

若是条件复杂,执行语句单一,那么条件能够经过&&||、三元来简化,或者是平铺if-return,问题也迎刃而解。然而,条件复杂,执行语句大几率也是复杂的。

if (a === 1) {
  console.log(1);
} else if (arr.length === 3) {
  alert('this is length 3 arr');
} else if (a === 2 && b === 1) {
  console.log(2);
  console.info('haha');
} else if (a === 2) {
  console.log(222);
  document.title = 'a = 2';
} else if (arr.length) {
  console.error('arr is not empty');
}
复制代码

都没有规律可循,那么就真的没有进一步方案了。可是咱们观察一下,发现一些条件是有交集的,如a === x,咱们能够把这种类型的if抽出来:

const handleA = {
    1: () => {
        console.log(1);
    },
    2: () => {
        if (b === 1) {
            console.log(2);
            console.info('haha');
        } else {
            console.log(222);
            document.title = 'a = 2';
        }
    }
}
const handleArrLen = {
    3: () => {
        alert('this is length 3 arr');
    }
}
if (handleA[a]) {
    handleA[a]()
} else if (arr.length) {
    ;(handleArrLen[arr.length] || () => {
        console.log(222);
        document.title = 'a = 2';
    })()
}
复制代码

这样子,能够把逻辑模块化,增长可读性和可维护性,可是牺牲了精简性。

注意,上面这样子条件模块化了,意味着同一类的条件都会归到一块儿。若是业务逻辑对if条件有严格要求的,好比必定要先判断a === 1,再看看arr.length === 3,再看a === 2 && b === 1,按照这样的顺序,那就不能这样作了

还有一种状况,就是if里面直接调用已经封装好的函数,没有其余语句(其实就至关于退化为条件复杂,执行语句简单了):

if (a === 1) {
  f1()
} else if (arr.length === 3) {
  f2()
} else if (a === 2 && b === 1) {
  f3()
} else if (a === 2) {
  f4()
} else if (arr.length) {
  f5()
}
复制代码

这种状况(前提条件,不会常常改这里,若是是常常改,仍是会吐血的),减小if后也不赖:

const index = [a === 1, arr.length === 3, a === 2 && b === 1, a === 2, arr.length].findIndex(Boolean)
if (index !== -1) {
  [f1, f2, f3, f4, f5][index]()
}
复制代码

若是全部的else if都是互斥的,没有交集,那么换成if-return更好

if (a) {
    // do xx about a
    return
}
if (b) {
    // do xx about b
    return
}

复制代码

小结:若是条件复杂,执行语句单一,建议优化指数: ★★★★★;若是执行语句也复杂,当条件能够模块化的且没有顺序要求,建议优化指数: ★★★★。当条件有严格顺序要求、无规律可循,不建议强行减小if-else

if条件有嵌套

嵌套实际上就是平铺的加强,平铺嵌套平铺,咱们能够看成是多个if平铺条件复杂的状况来看。例若有以下代码,咱们寻找一些规律来优化一下

if (id === 1) {
    console.log(1);
    if (type === 2) {
      console.log('type2');
    } else {
      console.log('type3');
    }
  } else if (id === 2) {
    console.log(2);
    if (type === 3) {
      console.log('id2 type3');
      if (ext === 1) {
        console.log('ext1');
      }
    }
  } else {
    console.log('other id');
  }
复制代码

根据id划分:牺牲了其余因素的可读性,但对于id的维护性加强了

const handleId = {
    1: () => {
        console.log(1)
        console.log(type === 2 ? 'type2' : 'type3')
    },
    2: () => {
        console.log(2)
        // 这里建议优化指数为★★,可能可读性低,因此保持现状也行
        // eslint一开,这套凉凉,必须写回普通if-else
        type === 3 && (console.log('id2 type3'), ext === 1) && console.log('ext1')
    }
}
handleId[type] ? handleId[type]() : console.log('other id')
复制代码

若是此时根据type划分,那么难度大大增长了,这就是人人害怕的重构了。若是后面业务逻辑,的确是以type为主导的,那重构也是迟早的事情了。因此,前期的设计以及产品逻辑,将会决定后面的维护舒服不舒服了

小结: if条件有嵌套状况,拆分if,其实就是平铺的if嵌套平铺的if,若是有规律可循,那么按照前面的平铺来减小if。若是没有规律、也不是逻辑侧重的点,那么就不建议减小if了

总结

  • 条件简单,执行语句单一,强烈建议减小if-else来优化,用条件驱动结果(&& ||三元或者是本身写小逻辑)
  • 条件简单,执行语句复杂,可保持现状或者换成switch,若是不复杂可使用map映射
  • 条件复杂,执行语句单一,强烈建议减小if-else来优化;若是执行语句也复杂,当条件能够模块化的且没有顺序要求,比较建议优化。当条件有严格顺序要求、无规律可循,不建议任何改动
  • 嵌套if,拆分为平铺if来判断如何优化或者不改动

关注公众号《不同的前端》,以不同的视角学习前端,快速成长,一块儿把玩最新的技术、探索各类黑科技

相关文章
相关标签/搜索