前言
應該有不少同學有遇到過充斥著if else的程式碼,面對這樣的一團亂麻,簡單粗暴地繼續增量修改常常只會讓複雜度越來越高,可讀性越來越差。那麼是時候重構了,花幾分鐘看看這篇文章, 說不定對你有一丟丟幫助。
場景一: 根據status顯示對應名稱
#最佳化方案1:object物件
const statusStr = { '1': '待付款', '2': '待发货', '3': '已发货', '4': '交易完成', '5': '交易关闭', 'default': '', } const getStatus = (status) =>{ return statusStr[status] || statusStr['default'] }
將判斷條件作為物件的屬性名,將處理邏輯作為物件的屬性值,在按鈕點擊的時候,透過物件屬性尋找的方式來進行邏輯判斷.
最佳化方案2:Map物件
const statusStr = new map([ '1': ['待付款'], '2': ['待发货'], '3': ['已发货'], '4': ['交易完成'], '5': ['交易关闭'], 'default': [''], ]) const getStatus = (status) =>{ let actions = statusStr.get(status) || statusStr.get('default') return actions[0]; }
這樣寫用到了es6裡的Map對象,那麼Map對象和Object對像有什麼差別呢?
一個物件通常都有自己的原型,所以一個物件總是有一個"prototype"鍵。 一個物件的鍵只能是字串或Symbols,但一個Map的鍵可以是任意值。 你可以透過size屬性很容易地得到一個Map的鍵值對個數,而物件的鍵值對個數只能手動確認。
場景二:多個condition對應名稱
#現在把問題升級一下, 以前按鈕點擊時候只需要判斷status,現在還需要判斷使用者的身分:
「舉個栗子:」
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 } } }
上面的例子我們可以看到,當你的邏輯升級為二元判斷時,你的判斷量會加倍,你的程式碼量也會加倍,這時怎麼寫更清爽呢?
優化方案1: condition用字元拼接形式存在Map物件裡
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*/}], ]) const onButtonClick = (identity,status)=>{ let action = actions.get(`${identity}_${status}`) || actions.get('default') action.call(this) }
上述程式碼核心邏輯是:把兩個條件拼接成字串,並通過以條件拼接字串作為鍵,以處理函數作為值的Map物件進行尋找並執行,這種寫法在多元條件判斷時候尤其好用。
優化方案2:condition用字元拼接形式存在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) }
優化方案3: 將condition用Object物件形式存在Map物件裡
可能用查詢條件拼成字串有點彆扭,那還有一個方案,就是用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)) }
#場景三:根據status做出對應操作
「舉個栗子:」
function init () { if (isAnswer === 1) { if (isOldUser === 1) { // ... } else if (isOldUser === 2) { // ... } } else if (isAnswer === 2) { if (isOldUser === 1) { // ... } else if (isOldUser === 2) { // ... } } else if (isAnswer === 3) { if (isOldUser === 1) { // ... } else if (isOldUser === 2) { // ... } } }
#最佳化方案1: 找出表,職責鏈查找表
const rules = [ { match (an, old) {if (an === 1) {return true}}, action (an, old) { if (old === 1) {// ...} else if (old === 2) {// ...} } }, { match (an, old) { if (an === 2) {return true } }, action (an, old) { if (old === 1) {// ...} else if (old === 2) {// ...} } }, { match (an, old) {if (an === 3) {return true}}, action (an, old) { if (old === 1) {// ...} else if (old === 2) {// ...} } } ] function init (an, old) { for (let i = 0; i < rules.length; i++) { // 如果返回true if (rules[i].match(an, old)) { rules[i].action(an, old) } } } init(isAnswer, isOldUser)
雖然可能看著是治標不治本,其實不然,init函數的複雜度大大的降低了。我們已經把控制流程的複雜邏輯,拆分到determineAction函數中
最佳化方案2: 函數式程式設計
import R from 'ramda' var fn = R.cond([ [R.equals(0), R.always('water freezes at 0°C')], [R.equals(100), R.always('water boils at 100°C')], [R.T, temp => 'nothing special happens at ' + temp + '°C'] ]); fn(0); //=> 'water freezes at 0°C' fn(50); //=> 'nothing special happens at 50°C' fn(100); //=> 'water boils at 100°C'
場景四:依照範圍去進行不同處理
「舉個栗子:」例如大家可能會遇到類似下面的需求:例如某平台的信用分數評級,超過700-950,就是信用極佳,650-700信用優秀,600-650信用良好,550-600信用中等,350-550信用較差。
function showGrace(grace) { let _level=''; if(grace>=700){ _level='信用极好' } else if(grace>=650){ _level='信用优秀' } else if(grace>=600){ _level='信用良好' } else if(grace>=550){ _level='信用中等' } else{ _level='信用较差' } return _level; }
最佳化方案1: 用look-up表,把設定資料和業務邏輯分開
function showGrace(grace,level,levelForGrace) { for(let i=0;i<level.length;i++){ if(grace>=level[i]){ return levelForGrace[i]; } } //如果不存在,那么就是分数很低,返回最后一个 return levelForGrace[levelForGrace.length-1]; } let graceForLevel=[700,650,600,550]; let levelText=['信用极好','信用优秀','信用良好','信用中等','信用较差'];
小結
#很多情況下我們都可以使用更靈活的方式去替代if else以及switch, 但也不是所有的if else都需要替代, 視情況而定。
更多相關免費學習:javascript#(影片)
以上是消除if else, 讓你的程式碼看起來更優雅的詳細內容。更多資訊請關注PHP中文網其他相關文章!