如何避免使用过多的 if else?

一、引言

相信大家听说过回调地狱——回调函数层层嵌套,极大降低代码可读性。其实,if-else层层嵌套,如下图所示,也会形成类似回调地狱的情况。

当业务比较复杂,判断条件比较多,项目进度比较赶时,特别容易使用过多if-else。其弊端挺多的,如代码可读性差、代码混乱、复杂度高、影响开发效率、维护成本高等。

因此,我们在日常编码时,有必要采取一些措施避免这些问题。本文的初衷不是建议大家完全不用if-else,而是希望我们能够在学会更多解决方案后更优雅地编码。


二、8种if-else的优化/替代方案

1. 使用排非策略[1]:!、!!

逻辑非(logic NOT),是逻辑运算中的一种,就是指本来值的反值。

当你想这么写时……

1、判断是否为空
if(value === null || value === NaN || value === 0 || value === ''|| value === undefined   )
{
……
}

2、判断是否数组是否含有符合某条件的元素
const name = arr.find(item => item.status === 'error')?.name;
if(name !== undefined && name !== ''){
……
}

不妨尝试这么写:

1、判断是否为空
if(!value){……}

2、判断是否数组是否含有符合某条件的元素
if(!!arr.find(item => item.status === 'error')?.name){……}


2. 使用条件(三元)运算符[2]: c ? t : f

三元运算符: condition ? exprIfTrue : exprIfFalse; 如果条件为真值,则执行冒号(:)前的表达式;若条件为假值,则执行最后的表达式。

当你想这么写时……

let beverage = '';
if(age > 20){
beverage = 'beer';
} else {
beverage = 'juice';
}

不妨尝试这么写:

const beverage = age > 20 ? 'beer' : 'juice';

tips: 建议只用一层三元运算符,多层嵌套可读性差。


3. 使用短路运算符:&&[3]、 ||[4]

  • && 为取假运算,从左到右依次判断,如果遇到一个假值,就返回假值,以后不再执行,否则返回最后一个真值;

  • || 为取真运算,从左到右依次判断,如果遇到一个真值,就返回真值,以后不再执行,否则返回最后一个假值。

当你想这么写时……

    if (isOnline){
    makeReservation(user);
    }

不妨尝试这么写:

 isOnline && makeReservation(user);


4. 使用 switch 语句[5]

当你想这么写时……

    let result;
    if (type === 'add'){
    result = a + b;
    } else if(type === 'subtract'){
    result = a - b;
    } else if(type === 'multiply'){
    result = a * b;
    } else if(type === 'divide'){
    result = a / b;
    } else {
    console.log('Calculation is not recognized');
    }

不妨尝试这么写:

let result;
switch (type) {
   case 'add':
     result = a + b;
     break;
   case 'subtract':
     result = a - b;
     break;
   case 'multiply':
     result = a * b;
     break;
   case 'divide':
     result = a / b;
     break;
   default:
    console.log('Calculation is not recognized');
}

个人认为,对于这类比较简单的判断,用switch语句虽然不会减少代码量,但是会更清晰喔。


5. 定义相关函数拆分逻辑,简化代码

当你想这么写时……

function itemDropped(item, location) {
    if (!item) {
        return false;
    } else if (outOfBounds(location) {
        var error = outOfBounds;
        server.notify(item, error);
        items.resetAll();
        return false;
    } else {
        animateCanvas();
        server.notify(item, location);
        return true;
    }
}

不妨尝试这么写:

function itemDropped(item, location) {
  const dropOut = function () {
    server.notify(item, outOfBounds);
    items.resetAll();
    return false;
  };

  const dropIn = function () {
    animateCanvas();
    server.notify(item, location);
    return true;
  };

  return !!item && (outOfBounds(location) ? dropOut() : dropIn());
}

细心的朋友会发现,在这个例子中,同时使用了前文提及的优化方案。这说明我们在编码时可以根据实际情况混合使用多种解决方案。


6. 将函数定义为对象,通过穷举查找对应的处理方法

  • 定义普通对象

    对于方案3的例子,不妨尝试这么写:

function calculate(action, num1, num2) {
  const actions = {
    add: (a, b) => a + b,
    subtract: (a, b) => a - b,
    multiply: (a, b) => a * b,
    divide: (a, b) => a / b,
  };
​
  return actions[action]?.(num1, num2) ?? "Calculation is not recognized";
}

  • 定义 Map 对象

    普通对象的键需要是字符串,而 Map[6] 对象的键可以是一个对象、数组或者更多类型,更加灵活。

let statusMap = new Map([
  [
    { role: "打工人", status: "1" },
    () => { },
  ],
  [
    { role: "打工人", status: "2" },
    () => { },
  ],
  [
    { role: "老板娘", status: "1" },
    () => { },
  ],
]);

let getStatus = function (role, status) {
  statusMap.forEach((value, key) => {
    if (JSON.stringify(key) === JSON.stringify({ role, status })) {
      value();
    }
  });
};

getStatus("打工人", "1"); 

tips: JSON.stringify()[7]可用于深比较/深拷贝。


7. 使用责任链模式[8]

责任链模式:将整个处理的逻辑改写成一条责任传递链,请求在这条链上传递,直到有一个对象处理这个请求。

例如 JS 中的_事件冒泡_。

简单来说,_事件冒泡_就是在一个对象上绑定事件,如果定义了事件的处理程序,就会调用处理程序。相反没有定义的话,这个事件会向对象的父级传播,直到事件被执行,最后到达最外层,document对象上。

这意味着,在这种模式下,总会有程序处理该事件。

再举个🌰,当你想这么写时……

function demo (a, b, c) {
  if (f(a, b, c)) {
    if (g(a, b, c)) {
      
    }
    
    else if (h(a, b, c)) {
      
    }
    
  } else if (j(a, b, c)) {
    
  } else if (k(a, b, c)) {
    
  }
}

不妨参考这种写法:

const rules = [
  {
    match: function (a, b, c) {  },
    action: function (a, b, c) {  }
  },
  {
    match: function (a, b, c) {  },
    action: function (a, b, c) {  }
  },
  {
    match: function (a, b, c) {  },
    action: function (a, b, c) {  }
  }
  
]


function demo (a, b, c) {
  for (let i = 0; i < rules.length; i++) {
    if (rules[i].match(a, b, c)) {
      return rules[i].action(a, b, c)
    }
  }
}

引申话题——如何降低if else代码的复杂度?

相关文章阅读: 如何无痛降低 if else 面条代码复杂度[9] 建议多读几次!!!


8. 策略模式+工厂方法

因为此法比较复杂,此文暂时不做详细介绍。

详细可参考文章优化方案 8 if-else 代码优化的八种方案[10]。

三、小结

本文粗略介绍了8种优化/替代if-else的方法,希望能给你日常编码带来一些启示😄。

正如开头所说,我们的目的不是消灭代码中的if-else,而是让我们在学会更多解决方案的基础上,根据实际情况选择更优的编码方式。因此,当你发现自己的代码里面存在特别多的if-else或当你想用if-else时,不妨停下来思考一下——如何能写得更优雅、更方便日后维护呢

四、参考与感谢

  1. 优化 JS 中过多的使用 IF 语句[11]

  2. 短路运算符(逻辑与&& 和 逻辑或||)[12]

  3. 如何对多个 if-else 判断进行优化[13]

  4. if-else 代码优化的八种方案[14]

  5. 如何替换项目中的if-else和switch[15]

  6. 如何无痛降低 if else 面条代码复杂度[16]

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
可以使用策略模式来避免过多的if else。策略模式将每种校验规则封装成一个独立的类,然后由一个上下文对象来调用相应的校验规则类进行数据校验。这样可以将复杂的if else逻辑转化为简单的对象调用,提高代码的可读性和可维护性。 例如,假设需要对用户输入的手机号进行校验,可以定义一个手机号校验策略接口: ``` public interface PhoneValidationStrategy { boolean validate(String phone); } ``` 然后定义不同的手机号校验规则类实现该接口: ``` public class ChinaPhoneValidationStrategy implements PhoneValidationStrategy { @Override public boolean validate(String phone) { // 校验中国手机号的规则 } } public class USPhoneValidationStrategy implements PhoneValidationStrategy { @Override public boolean validate(String phone) { // 校验美国手机号的规则 } } // 其他国家的手机号校验规则类... ``` 最后,在上下文对象中调用相应的校验规则类进行数据校验: ``` public class PhoneValidator { private PhoneValidationStrategy strategy; public PhoneValidator(PhoneValidationStrategy strategy) { this.strategy = strategy; } public boolean validate(String phone) { return strategy.validate(phone); } } ``` 这样,在使用时只需要创建相应的校验规则类和上下文对象即可: ``` PhoneValidationStrategy strategy = new ChinaPhoneValidationStrategy(); PhoneValidator validator = new PhoneValidator(strategy); boolean isValid = validator.validate("13800138000"); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Web面试那些事儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值