面對過多的if-else,代碼可能看起來比較冗餘,搞不好又是一張被人到處轉發的“我們項目幾百幾千行if”的圖。但是經過各種設計模式和封裝,if大大減少,但可讀性可能稍微降低了,而且比較抽象。那我們應該如何取舍呢
抛開其他因素,如果if-else過多,可讀性也許會好也可能會降低,可維護性也是或高或低;如果if-else少,代碼高度抽象,可讀性會低或者不變,可維護性可能會高也可能會低。這裡大概可能會有幾種情況
這種情況,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')
}
條件單一、執行語句單一的情況,建議優化指數:★★★★★
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')
}
精簡代碼:
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(由于條件處理複雜,所以條件上沒有優化空間了,必須寫出來)
這種場景,平時應該會比較常見轉為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的
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條件有嵌套小結:如果條件複雜,執行語句單一,建議優化指數: ★★★★★;如果執行語句也複雜,當條件可以模塊化的且沒有順序要求,建議優化指數: ★★★★。當條件有嚴格順序要求、無規律可循,不建議強行減少if-else
嵌套實際上就是平鋪的增強,平鋪嵌套平鋪,我們可以當作是多個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了
作者:lhyt鍊接:https://juejin.im/post/5e86a0e56fb9a03c4a4966fb
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!