浅谈优化if...else

4,075 阅读5分钟

为何要优化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源码