引言
我們編寫js代碼時經(jīng)常遇到復雜邏輯判斷的情況莺褒,通常大家可以用if/else或者switch來實現(xiàn)多個條件判斷欧啤,但這樣會有個問題褪秀,隨著邏輯復雜度的增加怀樟,代碼中的if/else/switch會變得越來越臃腫末盔,如何更優(yōu)雅的寫判斷邏輯筑舅,本文帶你試一下。
例子
先看一段代碼:
/**
* 按鈕點擊事件
* @param {number} status 活動狀態(tài):1 開團進行中 2 開團失敗 3 商品售罄 4 開團成功 5 系統(tǒng)取消
*/
const onButtonClick = (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');
}
}
通過代碼可以看到這個按鈕的點擊邏輯:根據(jù)不同活動狀態(tài)做兩件事情陨舱,發(fā)送日志埋點和跳轉(zhuǎn)到對應頁面翠拣,大家可以很輕易的提出這段代碼的改寫方案,switch出場:
/**
* 按鈕點擊事件
* @param {number} status 活動狀態(tài):1 開團進行中 2 開團失敗 3 商品售罄 4 開團成功 5 系統(tǒng)取消
*/
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
}
}
這樣看起來比if/else清晰多了游盲,細心的同學也發(fā)現(xiàn)了小技巧误墓,case 2和case 3邏輯一樣的時候,可以省去執(zhí)行語句和break益缎,則case 2的情況自動執(zhí)行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 活動狀態(tài):1開團進行中 2開團失敗 3 商品售罄 4 開團成功 5 系統(tǒng)取消
*/
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 活動狀態(tài):1 開團進行中 2 開團失敗 3 商品售罄 4 開團成功 5 系統(tǒng)取消
*/
const onButtonClick = (status) => {
let action = actions.get(status) || actions.get('default')
sendLog(action[0])
jumpTo(action[1])
}
這樣寫用到了es6里的Map對象驳癌,是不是更爽了?Map對象和Object對象有什么區(qū)別呢役听?
- 一個對象通常都有自己的原型颓鲜,所以一個對象總有一個"prototype"鍵表窘。
- 一個對象的鍵只能是字符串或者Symbols,但一個Map的鍵可以是任意值甜滨。
- 你可以通過size屬性很容易地得到一個Map的鍵值對個數(shù)乐严,而對象的鍵值對個數(shù)只能手動確認。
我們需要把問題升級一下衣摩,以前按鈕點擊時候只需要判斷status昂验,現(xiàn)在還需要判斷用戶的身份:
/**
* 按鈕點擊事件
* @param {number} status 活動狀態(tài):1開團進行中 2開團失敗 3 開團成功 4 商品售罄 5 有庫存未開團
* @param {string} identity 身份標識:guest客態(tài) master主態(tài)
*/
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客態(tài) master主態(tài)
* @param {number} status 活動狀態(tài):1 開團進行中 2 開團失敗 3 開團成功 4 商品售罄 5 有庫存未開團
*/
const onButtonClick = (identity, status) => {
let action = actions.get(`${identity}_${status}`) || actions.get('default')
action.call(this)
}
上述代碼核心邏輯是:把兩個條件拼接成字符串,并通過以條件拼接字符串作為鍵抛虫,以處理函數(shù)作為值的Map對象進行查找并執(zhí)行松靡,這種寫法在多元條件判斷時候尤其好用。
當然上述代碼如果用Object對象來實現(xiàn)也是類似的:
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的區(qū)別阅茶,Map可以用任何類型的數(shù)據(jù)作為key。
我們現(xiàn)在再將難度升級一點點谅海,假如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 */ }],
//...
])
好一點的寫法是將處理邏輯函數(shù)進行緩存:
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))
}
這樣寫已經(jīng)能滿足日常需求了扭吁,但認真一點講撞蜂,上面重寫了4次functionA還是有點不爽,假如判斷條件變得特別復雜侥袜,比如identity有3種狀態(tài)蝌诡,status有10種狀態(tài),那你需要定義30條處理邏輯枫吧,而往往這些邏輯里面很多都是相同的浦旱,這似乎也是筆者不想接受的,那可以這樣實現(xiàn):
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的優(yōu)勢更加凸顯九杂,可以用正則類型作為key了颁湖,這樣就有了無限可能另绩,假如需求變成吨拗,凡是guest情況都要發(fā)送一個日志埋點,不同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))
}
也就是說利用數(shù)組循環(huán)的特性研铆,符合正則條件的邏輯都會被執(zhí)行俘侠,那就可以同時執(zhí)行公共邏輯和單獨邏輯羡亩,因為正則的存在顾腊,你可以打開想象力解鎖更多的玩法,本文就不贅述了吴侦。
總結(jié)
本文已經(jīng)教你了8種邏輯判斷寫法屋休,包括:
- if/else
- switch
- 一元判斷時:存到Object里
- 一元判斷時:存到Map里
- 多元判斷時:將condition拼接成字符串存到Object里
- 多元判斷時:將condition拼接成字符串存到Map里
- 多元判斷時:將condition存為Object存到Map里
- 多元判斷時:將condition寫作正則存到Map里
轉(zhuǎn)自作者:大轉(zhuǎn)轉(zhuǎn)FE/Think
原創(chuàng)地址