讓你的代碼更加的的完美
每天學習一點,進步一點,加油!!
複雜判斷的更優雅寫法
前言
我們編寫js代碼時經常遇到複雜邏輯判斷的情況,通常大家可以用if/else或者switch來實現多個條件判斷,但這樣會有個問題,隨着邏輯複雜度的增加,代碼中的if/else/switch會變得越來越臃腫,越來越看不懂,那麼如何更優雅的寫判斷邏輯,本文帶你試一下。
舉個例子 先看一段代碼
/**
* 按鈕點擊事件
* @param {number} status 活動狀態:1 開團進行中 2 開團失敗 3 商品售罄 4 開團成功 5 系統取消
*
*/
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");
}
};
通過代碼可以看到這個按鈕的點擊邏輯:根據不同活動狀態做兩件事情,發送日誌埋點和跳轉到對應頁面,大家可以很輕易的提出這段代碼的改寫方案,switch出場:
使用switch
/**
* 按鈕點擊事件
* @param {number} status 活動狀態:1 開團進行中 2 開團失敗 3 商品售罄 4 開團成功 5 系統取消
*/
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清晰多了,細心的同學也發現了小技巧,case 2和case 3邏輯一樣的時候,可以省去執行語句和break,則case 2的情況自動執行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 活動狀態:1開團進行中 2開團失敗 3 商品售罄 4 開團成功 5 系統取消
*/
const onButtonClick = (status) => {
let action = actions[status] || actions["default"],
logName = action[0],
pageName = action[1];
sendLog(logName);
jumpTo(pageName);
};
上面代碼確實看起來更清爽了,這種方法的聰明之處在於:將判斷條件作爲對象的屬性名,將處理邏輯作爲對象的屬性值,在按鈕點擊的時候,通過對象屬性查找的方式來進行邏輯判斷,這種寫法特別適合一元條件判斷的情況。
是不是還有其他寫法呢?有的:
使用Map數據結構
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 活動狀態:1 開團進行中 2 開團失敗 3 商品售罄 4 開團成功 5 系統取消
*/
const onButtonClick = (status) => {
let action = actions.get(status) || actions.get("default");
sendLog(action[0]);
jumpTo(action[1]);
};
這樣寫用到了es6裏的Map對象,是不是更爽了?Map對象和Object對象有什麼區別呢?
- 一個對象通常都有自己的原型,所以一個對象總有一個"prototype"鍵。
- 一個對象的鍵只能是字符串或者Symbols,但一個Map的鍵可以是任意值。
- 你可以通過size屬性很容易地得到一個Map的鍵值對個數,而對象的鍵值對個數只能手動確認。
我們需要把問題升級一下,以前按鈕點擊時候只需要判斷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結構
當然上述代碼如果用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與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, },() => {/* functionA */}, ],
//...
]);
好一點的寫法是將處理邏輯函數進行緩存:
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))
}
這裏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
-
一元判斷時:存到Object裏
-
一元判斷時:存到Map裏
-
多元判斷時:將condition拼接成字符串存到Object裏
-
多元判斷時:將condition拼接成字符串存到Map裏
-
多元判斷時:將condition存爲Object存到Map裏
-
多元判斷時:將condition寫作正則存到Map裏
後記
至此,本文也將告一段落,願你未來的人生裏,不只是有if/else/switch。