[译] 支持 JavaScript 三元运算符

几个月前,我在 Hacker News 上浏览到一篇(现已删除)关于不要使用 if 语句的文章。若是你像我同样对这个观点还不太了解,那值得你去看一下。只要在 Hacker News 上搜索 “if 语句”,你就会看到一篇文章说:“你可能不须要 if 语句”,或者是称 if 语句为 “可疑代码”,甚至是 “有害代码” 的文章。听着,你应该知道不一样的编程思想都是值得尊重的,尽管他们宣称使用 if 会伤害到别人。javascript

若是这对你来讲还不够,还有 “if 运动”。若是你加入,你会在网站上看到一个漂亮的横幅,并且你的名字也会在上面。对!若是你加入的话,那会是多么的有意思。css

当我第一次遇到这种奇怪的 “反对 if” 现象时,我以为颇有趣,但可能只不过是一些人在网上发疯了。你只须要谷歌一下,就能找到对任何事都疯狂的人。好比这个讨厌小猫的人。KITTENShtml

一段时间后,我看了 Linus Torvald 的 TED 演讲。在那次演讲中,他展现了两张幻灯片。第一张幻灯片贴出了他认为是 “bad taste” 的代码。前端

第二个是相同功能的代码,可是在 Linus 看来是 “good taste”。java

我意识到 Linus 是一个有点两极化的人物,你可能不一样意 “good taste” 与 “bad taste” 的描述。但我认为,通常来讲,第二张幻灯片对新手来讲更容易理解。它简洁、逻辑分支少且不包含 if 语句。我但愿个人代码应该是这样的。它不必定是什么高级的算法(永远不会),但我但愿它是逻辑简洁的,还记得 Smashing Pumpkins 乐队的 Billy Corgan 是如何描述的:linux

Cleanliness is godliness. And god is empty. Just like me.android

  • Billy Corgan, "Zero"

太可怕了!但这张专辑的确很棒。ios

除了让代码看起来杂乱以外,if 语句或 “分支逻辑” 还要求你的大脑同时去计算两条独立的逻辑路径,以及这些路径上可能发生的全部事情。若是你还嵌套使用了 if 语句,问题就会变得更加复杂,由于你在生成和计算一个决策树时,你的大脑必须像喝醉酒的猴子同样在决策树上跳来跳去。这样会大大下降代码的可读性。记住,在编写代码时,你应该考虑在你以后要去维护它的会是哪一个傻瓜。也许,就是你本身。git

做为必需要去维护本身代码的傻瓜,我最近一直有意识地避免在 JavaScript 中编写 if 语句。我并不老是可以成功,但我注意到,至少它迫使我从一个彻底不一样的角度来思考解决问题的方法。它使我成为一个更好的开发人员,由于它让我动脑子思考,不然我将会清闲地坐在豆袋上吃花生,而让 if 语句去完成全部的工做。github

在避免编写 if 语句的过程当中,我发现我喜欢 JavaScript 中的三元运算符和逻辑操做符组合使用的方式。我如今想建议你的是不太受欢迎的三元运算符,你可使用它与 &&|| 操做符一块儿编写一些很是简洁和具备可读性的代码。

不受欢迎的三元运算符

当我刚开始学习编程时,人们常说,“永远不要使用三元运算符”,它们太复杂了。因此我没有使用它。一直都没有。我历来没用过三元运算符也从未费心去质疑那些人的说法是否正确。

但如今,我不这么认为。

三元运算符只是去用一行代码来表示的 if 语句而已。绝对的说它们在任何状况下都太过复杂是不正确的。个人意思是,并非我要特立独行,但我能够彻底理解一个简单的三元运算符。当咱们说要永远去避免使用它们的时候,咱们是否是有点儿孩子气了呢?我认为一个结构良好的三元运算符是能够赛过一个 if 语句的。

让咱们举一个简单的例子。假设咱们有一个应用程序,咱们想在其中检查用户的登陆状态。若是已登陆,咱们就跳转到他们的我的主页。不然,咱们将跳转到登陆页面。下面是标准的 if 逻辑语句:

if (isLogggedIn) {
  navigateTo('profile');
}
else {
  navigateTo('unauthorized');
}
复制代码

用这 6 行代码来完成工做是很是简单的。6 行,请记住,你每运行 1 行代码,必须记住上面代码的运算结果以及它如何影响下面的代码。

下面是三元运算符的实现代码:

isLoggedIn ? navigateTo('profile') : navigateTo('unauthorized');
复制代码

你的大脑只须要计算这一行,而不是 6 行。你不须要在代码上下行之间移动,也不须要记住以前的内容。

不过,三元运算符的一个缺点是不能只针对一种状况进行逻辑判断。仍是刚刚的例子,若是你想在用户已登陆时跳转到他的我的主页,而若是没有登陆,则不采起任何操做,下面的代码将不起做用:

// !! 没法编译 !!
logggedIn ? navigateTo('profile')
复制代码

你不得不在这里使用 if 语句来完成工做。可是还有没有其余方法?

当你只想处理逻辑条件的一个分支但又不想使用 if 语句时,能够在 JavaScript 中使用这样一个技巧。你能够利用 JavaScript 中的 ||(或)和 &&(与)运算符一块儿工做的方式来实现这一点。

loggedIn && navigateTo('profile');
复制代码

这是如何实现的?

咱们在这里实际是在判断:”这两个语句都是 true 吗?” 若是第一项为 false,JavaScript 引擎就不会再去执行第二项了。由于其中一个已是 false 了,因此咱们知道结果不是 true。咱们利用了这个机制:若是第一项为 false,JavaScript 就不会去执行第二项。也就是说,“若是第一项为 true,那么就去执行第二项”。

若是我想换过来呢?咱们只想在用户没有登陆的状况下导航到用户主页,该怎么办呢?你能够直接在 loggedIn 变量前面使用 !,但也有另外一种方法。

loggedIn || navigateTo('profile');
复制代码

这句代码的意思是,“这两个语句会有一个是 true 吗?” 若是第一项是 false,就必须对第二项进行计算才能肯定。若是第一项为 true,就永远不会去执行第二项,由于已经知道其中一项为 true 了,所以整个语句的结果是 true

那下面这种方式如何呢?

if (!loggedIn) navigateTo('profile');
复制代码

不,在这种状况下,不推荐使用。因此,一旦知道可使用 &&|| 运算符来实现 if 语句的功能,就可使用它们来极大地简化咱们的代码。

下面是一个更复杂的例子。假设咱们有一个 login 函数,接收一个 user 对象做为参数。该对象可能为空,所以咱们须要检查 local storage,以查看用户是否在本地保存了会话。若是保存了,而且他是一个管理员用户,那么咱们将跳转到首页。不然,咱们将导航到另外一个页面,该页面提示用户还未经受权。下面是一个简单的 if 语句的实现。

function login(user) {
  if (!user) {
    user = getFromLocalStorage('user');
  }
  if (user) {
    if (user.loggedIn && user.isAdmin) {
      navigateTo('dashboard');
    }
    else {
      navigateTo('unauthorized');
    }
  }
  else {
    navigateTo('unauthorized');
  }
}
复制代码

噢。这也许很复杂,由于咱们对 user 对象作了不少是否为空的条件判断。我不但愿个人代码太过复杂,因此让咱们简化一下,由于这里有不少冗余的代码,咱们须要封装一些函数。

function checkUser(user) {
  if (!user) {
    user = getFromLocalStorage('user');
  }
  return user;
}

function checkAdmin(user) {
  if (user.isLoggedIn && user.isAdmin) {
    navigateTo('dashboard');
  }
  else {
    navigateTo('unauthorized');
  }
}

function login(user) {
  if (checkUser(user)) {
    checkAdmin(user);
  }
  else {
    navigateTo('unauthorized');
  }
}
复制代码

login 函数更简单了,但实际上代码更多了,当你考虑到整个代码而不只仅是 login 函数时,它并不必定是更简洁的。

我建议放弃使用 if 语句,而使用三元运算符,而且使用逻辑运算符,那么咱们能够在两行代码中完成全部这些操做。

function login(user) {
  user = user || getFromLocalStorage('user');
  user && (user.loggedIn && user.isAdmin) ? navigateTo('dashboard') : navigateTo('unauthorized')
}
复制代码

就是这样。全部烦人的 if 语句代码块折叠后都有两行。若是第二行代码看起来有点长,而且影响阅读,那么能够对其进行换行,使它们各自独处一行。

function login(user) {
  user = user || getFromLocalStorage("user");
  user && (user.loggedIn && user.isAdmin)
    ? navigateTo("dashboard")
    : navigateTo("unauthorized");
}
复制代码

若是你担忧别人可能不知道 &&|| 运算符在 JavaScript 中是如何工做的,请添加一些注释并格式化你的代码。

function login(user) {
  // 若是 user 为空,则检查 local storage
  // 查看是否保存了 user 对象
  user = user || getFromLocalStorage("user");
  
  // 确保 user 不为空,同时
  // 是登陆状态而且是管理员。不然,拒绝访问。
  user && (user.loggedIn && user.isAdmin)
    ? navigateTo("dashboard")
    : navigateTo("unauthorized");
}
复制代码

其余

也许,你还可使用一些其余技巧来处理 JavaScript 条件判断。

赋值

我最喜欢的技巧之一(在上面使用过)是一个单行代码来判断一个变量是否为空,而后若是为空就从新赋值。使用 || 运算符来完成。

user = user || getFromLocalStorage('user');
复制代码

你能够一直判断下去:

user = user || getFromLocalStorage('user') || await getFromDatabase('user') || new User();
复制代码

这也适用于三元运算符:

user = user ? getFromLocalStorage('user') : new User();
复制代码

组合条件

你能够为三元运算符提供多个执行语句。例如,若是咱们想要同时记录用户已经登陆的日志,而后跳转页面,就能够这样作,而不须要将全部这些操做封装到另外一个函数中。以下,使用括号括起来,并用逗号隔开。

isLoggedIn ? (log('Logged In'), navigateTo('dashboard')) : navigateTo('unauthorized');
复制代码

这也适用于 &&|| 操做符:

isLoggedIn && (log('Logged In'), navigateTo('dashboard'));
复制代码

嵌套三元运算符

你能够嵌套使用三元运算符。Eric Elliot 在关于三元运算符的文章中经过下面的例子说明了这一点:

const withTernary = ({
  conditionA, conditionB
}) => (
  (!conditionA)
    ? valueC
    : (conditionB)
    ? valueA
    : valueB
);
复制代码

Eric 在这里作的最有趣的一点是否认了第一个条件,这样你就不会把问号和冒号放在一块儿,否则就会难以阅读。我要更进一步,给代码添加一些缩进。同时我还添加了大括号和显式的返回语句,由于只有括号的话会让我觉得正在调用一个函数,实际上并无。

const withTernary = ({ conditionA, conditionB }) => {
  return (
    (!conditionA)
      ? valueC  
      : (conditionB)
        ? valueA
        : valueB
  )
}
复制代码

通常来讲,你不该该嵌套使用三元运算符或 if 语句。以上任何一篇 Hacker News 的文章都会让你羞愧地得出一样的结论。虽然我不是来羞辱你的,但我只想说,若是你再也不过分使用 if 语句,或许(只是或许)你之后会感谢你本身的。


这就是我对被误解的三元运算符和逻辑运算符的见解。我认为它们能够帮助你编写简洁、可读的代码,并彻底避免 if 语句。如今,咱们能够像 Linus Torvalds 同样用 “good taste” 来结束这一切了。我也能够早点退休,而后平静地度过余生。

若是发现译文存在错误或其余须要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可得到相应奖励积分。文章开头的 本文永久连接 即为本文在 GitHub 上的 MarkDown 连接。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

相关文章
相关标签/搜索