Stop using nested ifs!(译文)

本文探讨了在JavaScript代码中过度使用嵌套if带来的问题,提倡使用保护子句和责任链模式来提高代码可读性和维护性。作者展示了如何转换嵌套if结构,以及如何在处理复杂逻辑时保持DontRepeatYourself(DRY)原则。
摘要由CSDN通过智能技术生成

1

A typical use case for nested ifs: you want to perform all sorts of checks on some data to make sure it’s valid before finally doing something useful with it.

嵌套 if 的典型用例:您想对某些数据执行各种检查,以确保数据有效,然后再对其执行有用的操作。

Don’t do this(别这样做)! :

// JavaScript

function sendMoney(account, amount) {
  if (account.balance > amount) {
    if (amount > 0) {
      if (account.sender === 'user-token') {
        account.balance -= amount;
        console.log('Transfer completed');
      } else {
        console.log('Forbidden user');
      }
    } else {
      console.log('Invalid transfer amount');
    }
  } else {
    console.log('Insufficient funds');
  }
}

There’s a better way(又一个更好的办法):

// JavaScript

function sendMoney(account, amount) {
  if (account.balance < amount) {
    console.log('Insufficient funds');
    return;
  }
  if (amount <= 0) {
    console.log('Invalid transfer amount');
    return;
  }
  if (account.sender !== 'user-token') {
    console.log('Forbidden user');
    return;
  }
  account.balance -= amount;
  console.log('Transfer completed');
}

See how much cleaner it is? Instead of nesting ifs, we have multiple if statements that do a check and returnimmediately if the condition wasn’t met. In this pattern, we can call each of the ifstatements a guard clause.

看到它有多简洁了吗?我们不再嵌套 if,而是使用多个 if 语句进行检查,如果不满足条件,则立即返回。在这种模式中,我们可以将每一个 if语句称为保护子句。

If you do a lot of Node.js, you’ve probably seen this flow in Express middleware:

如果你经常使用 Node.js,你可能在 Express 中间件中见过这种流程:

// JavaScript

function authMiddleware(req, res, next) {
  const authToken = req.headers.authorization;
  if (!authToken) {
    return res.status(401).json({ error: 'Unauthorized' });
  }
  if (authToken !== 'secret-token') {
    return res.status(401).json({ error: 'Invalid token' });
  }
  if (req.query.admin === 'true') {
    req.isAdmin = true;
  }
  next();
}

It’s much better than this, right?(比这好多了,对吧?) :

// JavaScript

function authMiddleware(req, res, next) => {
  const authToken = req.headers.authorization;

  if (authToken) {
    if (authToken === 'secret-token') {
      if (req.query.admin === 'true') {
        req.isAdmin = true;
      }
      return next();
    } else {
      return res.status(401).json({ error: 'Invalid token' });
    }
  } else {
    return res.status(401).json({ error: 'Unauthorized' });
  }
};

You never go beyond one level of nesting. We can avoid the mess that we see in callback hell.

嵌套永远不会超过一级。我们可以避免在回调地狱中看到的混乱。

How to convert nested ifs to guard clauses(如何将嵌套的 if 转换为保护子句)

The logic for this for doing this is simple(这样做的逻辑很简单):

1. Find the innermost/success if(发现最深层/成功的 if )

Here we can clearly see it’s the cond3if. After this, ifwe don’t do any more checks and take the action we’ve always wanted to take.

在这里,我们可以清楚地看到是 cond3```if。在这之后,if````我们不再做任何检查,而是采取我们一直想采取的行动。

// JavaScript

function func(cond1, cond2, cond3) {
  if (cond1) {
    if (cond2) {
      if (cond3) {
        console.log('PASSED!');
        console.log('taking success action...');
      } else {
        console.log('failed condition 3');
      }
    } else {
      console.log('failed condition 2');
    }
  } else {
    console.log('failed condition 1');
  }
}

2. Invert the outermost if and return(反转最外层的 if 并返回)

Negate the ifcondition to put the elsestatements’ body in there and add a returnafter.

Delete the elsebraces (keep the body, it still contains the formerly nested ifs, and move the closing ifbrace to just after the return.

删除 if 条件,将 else 语句的主体放在其中,并在其后添加一个 return

删除 else 括号(保留主体,它仍然包含以前嵌套的 if,并将结尾的 if 括号移到 return 后面。

So:


// JavaScript

function func(cond1, cond2, cond3) {
  if (!cond1) { // 👈 inverted if condition
    // 👇 body of former else clause 
    console.log('failed condition 1'); 
    
    return; // 👈 exit on fail
  }
  
  // 👇 remaining nested ifs to convert to guard clauses
  if (cond2) {
    if (cond3) {
      console.log('PASSED!');
      console.log('taking success action...');
    } else {
      console.log('failed condition 3');
    }
  } else {
    console.log('failed condition 2');
  }
}

3. Do the same for each nested if until you reach the success if(对每个嵌套的 if 做同样的操作,直到成功```if``为止)

And then(然后是):


// JavaScript

function func(cond1, cond2, cond3) {
  if (!cond1) {
    console.log('failed condition 1');
    return;
  }
  if (!cond2) {
    console.log('failed condition 2');
    return;
  }
  
  // 👇 remaining nested ifs to convert
  if (cond3) {
    console.log('PASSED!');
    console.log('taking success action...');
  } else {
    console.log('failed condition 3');
  }
}

And finally(最后是):

// JavaScript

function func(cond1, cond2, cond3) {
  if (!cond1) {
    console.log('failed condition 1');
    return;
  }
  if (!cond2) {
    console.log('failed condition 2');
    return;
  }
  if (!cond3) {
    console.log('failed condition 3');
    return;
  }
  console.log('PASSED!');
  console.log('taking success action...');
}

Tip

Inverting if statements in VS Code is easy when you install the JavaScript Booster extension.

如果安装了 [JavaScript Booster 扩展],在 VS 代码中反转 if 语句就很容易了。

在 VS 代码中反转 if 语句很容易,只要安装了VS

在这里插入图片描述

Here we only had to put the cursor in the ifkeyword and activate the Show Code Actionscommand (Ctrl + .by default)

在这里,我们只需将光标放在关键字 if 中,然后激活 Show Code Actions 命令(默认为 Ctrl + .)

For a comprehensive of helpful VSCode extensions you should definitely install along with JavaScript Booster, check out this awesome article

如需了解一定要与 JavaScript Booster 一起安装的有用 VSCode 扩展,请查看这篇超赞文章.

Tip: split guard clauses into multiple functions to always avoid el(提示:将保护子句拆分为多个函数,以始终避免 elsee)

What if we want to do something other after checking the data in an if/else

如果我们在检查if/else中的数据后还想做其他事情,该怎么办?

For instance(例如):

// JavaScript

function func(cond1, cond2) {
  if (cond1) {
    if (cond2) {
      console.log('PASSED!');
      console.log('taking success action...');
    } else {
      console.log('failed condition 2');
    }
    console.log('after cond2 check');
  } else {
    console.log('failed condition 1');
  }
  console.log('after cond1 check');
}

In this function regardless of cond1's value, the 'after cond1 check'the line will still print. Similar thing for the cond2value if cond1is true.

In this case, it takes a bit more work to use guard clauses:

If we try to use guard clauses, we’ll end up repeating the lines that come after the if/elsechecks:

在此函数中,无论 cond1```的值如何,cond1 检查后````行仍将打印。如果 cond1true时,cond2的值也会类似。

在这种情况下,使用 guard 子句需要更多的工作:

如果我们尝试使用保护子句,最终会重复if/else检查之后的行:


function func(cond1, cond2) {
  if (!cond1) {
    console.log('failed condition 1');
    console.log('after cond1 check');
    return;
  }

  if (!cond2) {
    console.log('failed condition 2');
    console.log('after cond2 check');
    console.log('after cond1 check');
    return;
  }
  console.log('PASSED!');
  console.log('taking success action...');
  console.log('after cond2 check');
  console.log('after cond1 check');
}

func(true);

Because the lines must be printed, we print them in the guard clause before returning. And then, we print it in all(!) the following guard clauses. And once again, in the main function body if all the guard clauses were passed.

So what can we do about this? How can we use guard clauses and still stick to the DRY principle?

Well, we split the logic into multiple functions:

因为必须打印这些行,所以我们在返回之前在保护子句中打印这些行。然后,在接下来的所有(!)保护子句中打印。如果所有的保护子句都通过了,我们会再次在主函数体中打印。

那么我们能做些什么呢?我们怎样才能在使用守护子句的同时仍然坚持 DRY 原则呢?

我们可以将逻辑拆分成多个函数:


// JavaScript
function func(cond1, cond2) {
  checkCond1(cond1, cond2);
  console.log('after cond1 check');
}

function checkCond1(cond1, cond2) {
  if (!cond1) {
    console.log('failed condition 1');
    return;
  }
  checkCond2(cond2);
  console.log('after cond2 check');
}

function checkCond2(cond2) {
  if (!cond2) {
    console.log('failed condition 2');
    return;
  }
  console.log('PASSED!');
  console.log('taking success action...');
}


Let’s apply this to the Express middleware we saw earlier:

让我们把它应用到我们之前看到的 Express 中间件中:


// JavaScript

function authMiddleware(req, res, next) {
  checkAuthValidTokenAdmin(req, res, next);
}

function checkAuthValidTokenAdmin(req, res, next) {
  const authToken = req.headers.authorization;
  if (!authToken) {
    return res.status(401).json({ error: 'Unauthorized' });
  }
  checkValidTokenAdmin(req, res, next);
}
function checkValidTokenAdmin(req, res, next) {
  const authToken = req.headers.authorization;
  if (authToken !== 'secret-token') {
    return res.status(401).json({ error: 'Invalid token' });
  }
  checkAdmin(req, res, next);
}
function checkAdmin(req, res, next) {
  if (req.query.admin === 'true') {
    req.isAdmin = true;
  }
  next();
}


In a way, we’ve replaced the if/elsestatements with a chain of responsibility pattern. Of course, this might be an overkill for simple logic like a basic Express request middleware, but the advantage here is that it delegates each additional check to a separate function, separating responsibilities and preventing excess nesting.

Key takeaways(要点)

Using nested ifs in code can lead to complex and hard-to-maintain code. Instead, we can use guard clauses to make our code more readable and linear. We can apply guard clauses to different scenarios and split them into multiple functions to avoid repetition and split responsibilities. By adopting this pattern, we end up writing cleaner and more maintainable code.

Every Crazy Thing JavaScript Does(JavaScript 所做的每一件疯狂的事)

A captivating guide to the subtle caveats and lesser-known parts of JavaScript.

这是一本引人入胜的指南,介绍 JavaScript 的微妙注意事项和鲜为人知的部分。

图片

Sign up(登记) and receive a free copy immediately.

【参考文献】

标题:Stop using nested ifs. Do this instead

作者:Coding Beauty

日期:2023年4月4日

上述译文仅供参考,原文请查看下面链接,解释权归原作者所有

⚠️:文章翻译如有语法不准确或者内容纰漏,欢迎评论区指正。

【关于TalkX】

TalkX是一款基于GPT实现的IDE智能开发插件,专注于编程领域,是开发者在日常编码中提高编码效率及质量的辅助工具,TalkX常用的功能包括但不限于:解释代码、中英翻译、性能检查、安全检查、样式检查、优化并改进、提高可读性、清理代码、生成测试用例等。

TalkX建立了全球加速网络,不需要考虑网络环境,响应速度快,界面效果和交互体验更流畅。并为用户提供了OpenAI的密钥,不需要ApiKey,不需要自备账号,不需要魔法。

TalkX产品支持:JetBrains (包括 IntelliJ IDEA、PyCharm、WebStorm、Android Studio)、HBuilder、VS Code、Goland.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值