Skip to content
This page has been auto-translated and may contain errors.View in English

条件语句

docs.scrimba.com

一个总是做同样事情的程序用处不大。你想要一个页面向已登录的访客显示一条消息,向陌生人显示另一条消息,一个表单接受 18 岁但拒绝 12 岁,一个游戏报告你是赢了还是输了。所有这些都需要一种方式来提出问题并根据答案采取行动。这就是条件语句所做的:它们让你的代码根据某些内容是否为真来选择运行什么。

你已经有了这些部分。比较和逻辑运算符产生真假答案,本章讲的是如何根据这些答案采取行动。

仅在条件为真时运行代码

一个 if 语句 仅在条件为真时运行一个代码块。你写 if 这个单词,在括号中写条件,在花括号中写一个代码块。如果条件为真,代码块就会运行。如果不是,JavaScript 会跳过这个块并继续执行。

js
const age = 20;

if (age >= 18) {
  console.log("You can enter."); // You can enter.
}

条件 age >= 18 要么为真,要么为假。这里 age 是 20,所以它为真,消息就会打印。如果把 age 改为 12,条件就为假,代码块被跳过,什么都不会打印。

通常当条件为假时,你想要一个备选方案。这就是 else 的作用:

js
const age = 12;

if (age >= 18) {
  console.log("You can enter.");
} else {
  console.log("Come back when you are older."); // Come back when you are older.
}

恰好有一个代码块会运行,永远不会两个都运行。当你有两个以上的结果时,else if 让你检查另一个条件:

js
const score = 74;

if (score >= 90) {
  console.log("Grade: A");
} else if (score >= 70) {
  console.log("Grade: B"); // Grade: B
} else {
  console.log("Grade: C");
}

JavaScript 从上到下检查每个条件,并在找到第一个为真的条件时停止。74 的分数不 >= 90,但它是 >= 70,所以它打印 Grade: B 并从不查看 else

一个 if 语句 在其条件为真时运行一个代码块,当不为真时可选的 else 块会运行。用 else if 链接可以让你按顺序检查多个条件。

js
const score = 74;

if (score >= 90) {
  console.log("Grade: A");
} else if (score >= 70) {
  console.log("Grade: B"); // Grade: B
} else if (score >= 50) {
  console.log("Grade: C");
} else {
  console.log("Grade: F");
}

顺序很重要,这也是人们容易出错的地方。JavaScript 从上到下测试每个条件,对第一个为真的条件运行其代码块,然后跳过其余部分。这就是为什么范围从高到低的原因:一旦达到 score >= 70,74 就匹配了,链就停止了。如果你先写 score >= 50,74 就会匹配那个,而每个在 50 以上的人都会得到 C。

条件可以是产生真或假值的任何表达式,所以你可以用 operators 中的逻辑运算符组合检查:

js
const age = 20;
const hasTicket = true;

if (age >= 18 && hasTicket) {
  console.log("Enjoy the show."); // Enjoy the show.
} else {
  console.log("Entry refused.");
}

一个 if 语句 评估一个条件,当结果为真时运行其代码块,有可选的 else ifelse 分支用于其他情况。链是有序的:JavaScript 从上到下评估条件,执行第一个条件为真的代码块,然后退出整个链。

js
const score = 74;

if (score >= 90) {
  console.log("Grade: A");
} else if (score >= 70) {
  console.log("Grade: B"); // Grade: B
} else if (score >= 50) {
  console.log("Grade: C");
} else {
  console.log("Grade: F");
}

这个形式的两个特性值得记住。首先,分支是互斥的:恰好有一个会运行。这就是为什么这里的边界只需要一侧的原因。score >= 70 分支不需要 score < 90 来保护它,因为到达这一行已经意味着 score >= 90 测试失败了。添加冗余的上界是一个常见的本能,它让链更难读,而不是更安全。

其次,条件被强制转换为布尔值,所以它不必是一个字面比较。任何表达式都行,JavaScript 应用其真值和假值规则来决定。这种灵活性很有用,有时也是个陷阱,这就是为什么本章的高级部分在下面详细回到它。

js
const age = 20;
const hasTicket = true;

if (age >= 18 && hasTicket) {
  console.log("Enjoy the show."); // Enjoy the show.
} else {
  console.log("Entry refused.");
}
Juno仅在条件为真时运行代码 一个 if 仅在括号中的条件为真时运行其代码块,else 为它不为真的情况提供备选。当有两个以上的结果时,添加 else if。JavaScript 从上到下检查,并在第一个匹配时停止,所以把你的条件按有意义的顺序排列。
Juno仅在条件为真时运行代码 一个 if / else if / else 链运行第一个真条件的代码块并跳过其余的,所以顺序不是装饰,而是逻辑。范围从高到低恰好是因为这个原因。条件可以是任何解析为真或假的表达式,所以当你需要时用逻辑运算符组合检查。
Juno仅在条件为真时运行代码 链是互斥的,从上到下评估,所以每个分支只需要上面的分支还没有排除的边界。添加冗余的上界看起来是谨慎的,但实际上只是噪音。条件被强制转换为布尔值,这是一个特性,直到意外的值偷偷溜进来,所以记住真值和假值规则。

用三元运算符选择一个值

有时 if / else 做一件小事:在两个值之间选择。为此有一个更短的形式叫做 三元运算符。你写一个条件,一个 ?,条件为真时使用的值,一个 :,以及条件为假时使用的值。

js
const age = 20;
const message = age >= 18 ? "Welcome" : "Too young";

console.log(message); // Welcome

把它读作一个问题:age >= 18 吗?如果是,message 变成 "Welcome";如果不是,它变成 "Too young"。这与 if / else 会做出的决定相同,但只用一行。

三元运算符在你选择一个值来存储或打印时效果最好。当一个分支需要做几件事时,if 语句更清晰,所以只在每一侧都是一个单一的值时才使用三元运算符。

三元运算符 基于条件在两个值之间选择:condition ? valueIfTrue : valueIfFalse。与 if 语句不同,它是一个表达式,所以它产生一个值,你可以赋值或直接放入模板字面量。

js
const score = 82;
const passed = score >= 60 ? "pass" : "fail";

console.log(`Result: ${passed}`); // Result: pass

这是它发挥作用的地方。在一个变量中赋值两个值之一是三元运算符在一行中可以做的工作,而 if 版本在五行中展开相同的逻辑,并在两个分支中重复赋值:

js
// if 版本用更多仪式做同样的事情:
let passed;
if (score >= 60) {
  passed = "pass";
} else {
  passed = "fail";
}

它停止帮助的地方是当分支做的不仅仅是产生一个值时。如果任何一侧需要运行多个语句,或者你发现自己在另一个三元运算符内嵌套一个三元运算符,你获得的可读性就消失了。此时 if 语句是更清晰的选择。

三元运算符 是 JavaScript 中唯一接受三个操作数的运算符:condition ? valueIfTrue : valueIfFalse。使它有用的区别在于它是一个表达式,而不是一个语句。它评估为一个值,所以它适应语句不能去的地方:赋值的右侧、一个参数、一个模板字面量。

js
const score = 82;
const label = score >= 60 ? "pass" : "fail";

console.log(`Result: ${label}`); // Result: pass

当整个工作是在两个值之间选择时使用它,并且比 if 更倾向于它,因为它将赋值保留在一个地方而不是在两个分支中重复。使用三元运算符来选择一个值,而不是运行一个分支。

失败模式是嵌套。一个三元运算符,其分支本身是三元运算符,在技术上是可行的,但它强制读者从右到左解析,同时在头脑中保持多个条件:

js
// 难以阅读。这里倾向于 if 链或 switch。
const grade = score >= 90 ? "A" : score >= 70 ? "B" : score >= 50 ? "C" : "F";

那一行产生正确的答案,但花费下一个读者真正的努力。当一个决策有两个以上的结果时,一个 if 链或一个 switch 阐述得更简洁,这是下一部分。

Juno用三元运算符选择一个值 三元运算符是在两个值之间选择的紧凑方式:condition ? valueIfTrue : valueIfFalse。当你选择要存储或打印的东西时它闪闪发光。如果一个分支需要做的不仅仅是交回一个单一的值,使用完整的 if 代替,它会更清晰。
Juno用三元运算符选择一个值 三元运算符是一个表达式,所以它给回一个值,你可以赋值或放入模板字面量,而 if 语句做不到。这使它成为选择两个值之一的清晰选择,因为 if 版本在两个分支中重复赋值。一旦分支做了真正的工作,或者你开始嵌套三元运算符,就切换回 if
Juno用三元运算符选择一个值 三元运算符是一个表达式,所以它去的地方语句不能去:一个赋值、一个参数、一个模板字面量。用它来选择一个值,而不是运行一个分支,它保持可读性。在另一个内嵌套一个,它停止付出代价,所以多结果的决策属于 if 链或 switch

用 switch 对比单个值和固定情况

当你检查一个值是否与一个固定的可能性列表相符时,一个 switch 语句 可能比一个长的 if / else if 链更整洁。你给它要检查的值,然后列出要与之比较的情况。

js
const day = "Tue";

switch (day) {
  case "Sat":
    console.log("Weekend");
    break;
  case "Sun":
    console.log("Weekend");
    break;
  default:
    console.log("Weekday"); // Weekday
}

switch 依次将 day 与每个 case 比较。当它找到匹配时,它运行那个情况的代码。break 告诉它停止;没有它,JavaScript 会继续进入下一个情况。default 是备选,当没有情况匹配时运行,就像链中的最后一个 else

忘记 break 是经典的 switch 错误。漏掉它,你的代码运行匹配的情况,然后运行其下的每个情况,直到它碰到 break 或结束,这几乎从不是你的本意。

一个 switch 语句 将一个值与一组固定的情况比较。当每个分支都在测试同一个值是否相等时,它比一个长的 if / else if 链读起来更清晰。

js
const status = "shipped";

switch (status) {
  case "pending":
    console.log("Order received.");
    break;
  case "shipped":
    console.log("On its way."); // On its way.
    break;
  case "delivered":
    console.log("Delivered.");
    break;
  default:
    console.log("Unknown status.");
}

三件事承载逻辑。case 标记一个要匹配的值,break 结束匹配的情况,default 处理任何不匹配的。匹配使用严格相等(===),所以 case "1" 不会匹配数字 1

陷阱是贯穿。一个没有 breakcase 不会停止;执行继续进入以下情况,直到它碰到 break 或 switch 的结束。这通常是个 bug,但当多个情况共享一个结果时,它偶尔也很有用:

js
const day = "Sat";

switch (day) {
  case "Sat":
  case "Sun":
    console.log("Weekend"); // Weekend
    break;
  default:
    console.log("Weekday");
}

这里 "Sat" 匹配并贯穿到 "Sun" 的共享代码块,所以两个周末日子运行同一行。这是故意使用贯穿,它只有当空情况直接堆在共享块上方时才清晰可读。

一个 switch 语句 取一个单一值,并将其与一系列 case 标签比较,运行第一个匹配的代码块。比较是严格相等(===),这很重要:case "1" 不匹配数字 1,没有类型强制来拯救你。

js
const status = "shipped";

switch (status) {
  case "pending":
    console.log("Order received.");
    break;
  case "shipped":
    console.log("On its way."); // On its way.
    break;
  case "delivered":
    console.log("Delivered.");
    break;
  default:
    console.log("Unknown status.");
}

人们低估的机制是贯穿。一个 switch 不是运行一个情况然后停止;它跳到匹配的标签,然后直线执行下去,直到它碰到 break 或结束括号,沿途忽略情况边界。缺少 break 会运行匹配下的每个情况。这是大多数 switch bug 的来源,也是这种行为的一个故意使用,堆积标签以便多个值共享一个代码块:

js
const day = "Sat";

switch (day) {
  case "Sat":
  case "Sun":
    console.log("Weekend"); // Weekend
    break;
  default:
    console.log("Weekday");
}

default 不必来最后,不过把它放最后是保持 switch 可读的惯例。也要注意一个 switch 只测试一个值是否相等,所以一个像前面成绩例子那样的范围检查链不能变成 switch。这个限制正好是告诉你 switch 何时是正确工具的,本部分最后拆分的内容。

Juno用 switch 对比单个值和固定情况 一个 switch 检查一个值是否与一个 case 选项列表相符并运行匹配的,用 default 作为兜底。总是用 break 结束每个情况,否则 JavaScript 继续进入下面的情况。那个缺失的 break 是几乎每个人都至少犯过一次的错误。
Juno用 switch 对比单个值和固定情况 当每个分支都在测试同一个值是否相等并对比固定的情况时使用 switch,记住它用 === 匹配,所以 "1" 不是 1。每个情况需要一个 break 或它会贯穿到下一个。那个贯穿默认是个 bug,但堆积空情况在一个共享块上方是你想要它的唯一时间。
Juno用 switch 对比单个值和固定情况 一个 switch 用严格相等匹配一个值,然后向下运行直到 break,沿途忽略情况边界,所以缺少 break 运行匹配下的每个情况。堆积标签在一个共享块上方是对那个的唯一故意使用。因为它只测试单个值的相等性,一组范围检查保留为 if 链。

选择正确的工具

你现在有三种方式来做出决策,选择主要是关于合适性。当条件是范围或组合时,使用 if / else if / else 链用于大多数分支,特别是当条件是范围或组合时。当你选择两个值中的一个来存储或打印时使用三元运算符。当你检查一个值是否与一些固定的选项相符时使用 switch

这些都不比其他的更高级。选择最清楚地表述你的决策的那一个,如果你不确定,if 语句总是一个安全的默认值。

这三种形式重叠,所以有用的技能是将工具与决策的形状相匹配。一个 if 链处理任何事情,每当条件是范围(score >= 70)或组合(age >= 18 && hasTicket)时它是正确的选择。一个三元运算符用于在一个表达式中在两个值之间选择。当单个值对比几个固定情况是否相等时,一个 switch 适合,比如一个状态或一个日期名字。

一条规则可以防止许多缠绕的代码:选择读起来最简洁的形状,而不是最短的。一个嵌套的三元运算符比 if 链短但更难读,所以长度不是目标。下一个人的清晰度,通常是你在一个月后,是。

三个工具,它们之间的边界归结为决策的形状。一个 if 链是通用形式:它处理范围、组合和条件能表达的任何事情,所以每当分支不是全部在一个值上的相等测试时它是默认值。一个三元运算符缩小到一个单一的工作,在一个表达式中选择两个值之一,并且仅当两个分支保持简单时才付出代价。当一个值与固定的情况集相等比较时,一个 switch 适合。

有一个第四选项值得命名,即使它超出了范围:当你将一个值映射到一个结果时,一个查询对象经常击败所有三个。一个 switch 每个情况只赋值一个值,或一个长的 if 链的 value === "x" 测试,经常作为一个普通对象更清晰,你可以索引到它。一旦 objects 上桌,你会遇到那个模式;现在,注意到 switch 何时真正是一个表中的伪装。

两个更高级的习惯总结了这个。第一个是真值和假值,因为条件不需要是一个比较。JavaScript 强制条件为布尔值,出现假的值是一个固定的、短的集合:false0"" (空字符串)、nullundefinedNaN。所有其他的都是真值。这就是为什么 if (name) 是 "name 不是空的" 的常见简写,以及为什么它会让你惊讶:0 的值是一个真实数字但一个假条件,所以 if (count)count0 时跳过代码块。知道假值集,这些检查就停止是猜测了。

第二个是守卫子句。当一个决策真的是关于早期退出时,一个深层嵌套的 if 读起来不如首先检查退出条件并停止的好。那个 "检查坏的情况,停止,然后继续好的路径" 的形状是守卫子句,它保持主逻辑不缩进,而不是深埋在分支内。停止部分通常意味着从函数返回,这是下一章,functions,所以完整的模式在那里登陆。现在携带的想法是通过首先处理异常情况展平决策比嵌套正常情况更深更清晰。

Juno选择正确的工具if 链用于大多数决策,当你选择两个值之一时用三元运算符,当你将一个值与固定选项匹配时用 switch。没有哪个比其他的花哨。选择最清楚地表述你的决策的那一个,不确定时默认为 if
Juno选择正确的工具 将工具与形状相匹配:范围和组合用 if,选择两个值之一用三元运算符,与固定情况的相等性用 switch。选择读起来最简洁的形式,而不是最短的,因为一个嵌套三元运算符短但难跟随。下一个读者通常是你,所以为他们写。
Juno选择正确的工具if 链是通用工具,三元运算符用于选择两个值之一,switch 用于与固定集的相等性,一旦你有对象,一个查询对象经常击败值映射 switch。把假值集保持在你的头中(false0""nullundefinedNaN),所以当计数为零时一个裸 if (count) 不会让你惊讶。当一个决策真的是一个早期退出时,首先处理坏的情况并展平嵌套;完整的守卫子句与函数一起登陆。

条件语句从这里走向何方

条件语句是程序停止是固定脚本并开始响应其输入的方式。你会经常使用它们,它们与周围的一切结合:真假答案来自 operators,你在这里做出的决策一旦你可以用 loops 在数据上反复运行它们,并将它们打包在 functions 中时开始付出代价。

下一章,loops,添加了控制流的另一半:不仅选择是否运行代码,而是一次又一次地运行它,直到一个条件告诉你停止。