淺談優化if...else

santree發表於2018-12-16

為何要優化if..else

相信在做業務開發的時候大家總會因為瘋狂的需求變更或者時間的緊迫性不得已寫下許多垃圾程式碼,然後給自己留下個TODO:下次優化(實際上過後就忘了)(說的就是我沒錯了!)。

然後等到某一天這些程式碼出了問題之後,你來回看這段程式碼,oh~~~,那可能會讓你懷疑人生,所以今天就讓我們來聊聊在業務開發裡經常會使用到的if...else如何優化吧。

if (true) {
    if (true) {
        if (true) {
            if (true) {
                if (true) {
                    if (true) {
                        
                    }
                }
            }
        }
    }
}
複製程式碼

由簡到難

凡事都不是一口能吃成胖子的,我們先從最簡單的優化方式說起:

switch...case

相比if...else的多重巢狀,switch...case的鏈式呼叫當然更加淺顯易懂利於維護,當我們的條件比較單一但是數量較大時我們可以簡單地直接使用switch...case代替:

const a = 3;
      
//bad:
if (a === 1) {
    consoel.log('a > 1');
}
if (a === 2) {
    consoel.log('a > 2');
}
if (a === 3) {
    consoel.log('a > 3');
}
if (a === 4) {
    consoel.log('a > 4');
}
// good:
switch(a) {
    case 1:
        console.log('a = 1');
    case 2:
        console.log('a = 2');
    case 3:
        console.log('a = 3');
    defaut:
        console.log('blablabla');
}
複製程式碼

, 但是這樣寫還是讓我們覺得有些太多了,畢竟多寫了那麼多的case(滑稽),於是愛折騰的朋友就需要多動動腦子了。

使用object

相信大家都很瞭解,在js的物件裡是使用的key-value形式儲存資料的,而想我們上面的判斷條件只有一個引數,只需要判斷引數是否等於定值即可,那我們是不是可以在這上面做點文章呢?比如使用key代替判斷條件,使用value代替滿足條件時的判斷式:

const judegeMap = {
    1: () => { console.log('a = 1') },
    2: () => { console.log('a = 2') },
    3: () => { console.log('a = 3') },
    4: () => { console.log('a = 4') }
}

judgeMap[a]();
複製程式碼

看,這樣是不是就好多啦~,但是仔細想想,這樣就夠了嗎?在業務開發中,if的值永遠不可能單單只是一個定值,判斷式也千奇百怪,所以讓我們繼續

做出選擇(決策樹)

咳咳,似乎說了一個不得了的名詞。我們在這裡就不聊這個名詞的概念問題了,為什麼提及這位,只是因為我們接下來要聊的東西似乎挺像這麼一回事。

首先看看我們的if...else,仔細想想,它像不像是一顆樹呢:由一個一個頂層的判斷和其下其下包含的多個子級的判斷組成:

if (a > b) {
    if (a > 10) {
        if (a < 22) {
            ...     
        }
        ...
    }
    if (b > 12) {
        ...
    }
    ...
}
複製程式碼

由以上例子可以看出我們的決策樹由頂層的判斷:a>b和子集的判斷a > 10, a < 22, b > 12組合而成。整個樹的執行,又只需要從其中逐層向下摘取出滿足判定條件的部分。然後依次執行即可。由此我們可以做出如下優化:

function aIsBiggerThanb() {
    ...doSomething
}

function aIsBiggerThan10() {
    ...doSomething
}

function aIsSmallerThan22() {
    ...doSomething
}

function bIsBiggerThan12() {
    ...doSomething
}

const judgeArray = [
    {
        condition: a > b,
        callback: aIsBiggerThanb
    },
    {
        condition: a > b && a > 10,
        callback: aIsBiggerThan10
    },
    {
        condition: a > b && a > 10 && a < 22,
        callback: aIsSmallerThan22
    },
    {
        condition: a > b && b > 12,
        callback: bIsBiggerThan12
    }
];

const callbackArray = judgeArray.reduce((eventArray, item) => {
   item.condition &&  eventArray.push(item.callback)
   return eventArray;
}, [])

for (let id in callbackArray) {7lki9
    callbackArray[id]();
}
複製程式碼

這樣做可以大大增加你的程式碼的可讀性和可維護性,但是隨之會增加更多的程式碼編寫量,所以在這種情況下,我造了一個簡單的輪子解決這種問題,讓我們不再需要自己手動的編寫決策樹和正確決策的摘取過程,只需要關注業務的邏輯即可~

choicejs

install

你可以通過yarn或者npm安裝choicejs

$ npm install choicejs
$ yarn add choicejs
複製程式碼

require or import

const choicejs = require('choicejs').Choice;

import { Choice } from 'choicejs'
複製程式碼

usage

add(description: string, condition: boolean, callback: any, extend?: string)

該方法是用來增加你的選擇的,有四個引數,description代表你對當前選擇的描述,該項千萬不要重複,否則後面增加的選擇會覆蓋之前的,第二個選項就是判斷條件,第三個代指滿足判斷條件時的回撥方法,最後一個引數為可選引數,代指繼承於某項描述,就好比巢狀的if...else巢狀於某個條件一樣。

一個栗子:

const judgeTree = new Choice();

const logAisBiggerThan1() {
  console.log('a > 1')
};

const logAisSmallerThan9() {
  console.log('a < 9');
}

const a = 3;

judgeTree
  .add('biggerThan1', a > 1, logAisBiggerThan1)
  .add('smallerThan9', a < 9, logAisSmallerThan9, 'biggerThan1')
複製程式碼

use()

簡單暴力的方法,add用來定義,而use就是用來執行。如果沒有use,那麼定義好的決策樹就像是一個定義好的函式,沒有()它就毫無卵用~

栗子:

judgeTree.use();

// 注意,judgeTree 是支援鏈式呼叫的,所以放心大膽地將 use() 接在 add() 之後使用吧~
複製程式碼

destroy()

簡單的銷燬方法,使用完之後可以選擇清空當前例項中的所有資訊,通常作為最後一步使用,在此就不舉例啦~

具體使用例子,可以參照我在Runkit上的示例程式碼: Runkit示例

結語

雖然藉著文章厚顏無恥的推薦了一下自己的輪子,但是希望各位看官還是能從我的一些粗淺見解中學到一些東西,如果喜歡我的文章,麻煩請點個贊哦~

(當然,點個star我也是很開心的哈哈哈~)

choicejs原始碼

相關文章