多少算太多?
有些人认为这个数字是 一,你应该总是至少用一个三元运算符来替代任何单一的 if 语句。我不采取那么极端的方法,但我想突出一些方法来逃离常见的 if/else 斯巴达代码。
我相信很多开发者这么容易掉入 if/else 陷阱,并不是因为其他解决方案的复杂性,而是因为它遵循了一种非常自然的语言模式: if 某事做这个,else 做那个。
等等,什么是三元运算符?
如果你已经知道了,请跳过这部分。
三元运算符与 if/else 没有革命性的差别,它们都是条件操作,但三元运算符确实返回一个值,所以它可以直接用于赋值。
const greaterThanFive = 8 > 5 ? "yep" : "nope";
console.log(greaterThanFive); // 'yep'
基本模式就是一个条件,一个如果为真返回的值,和一个如果为假返回的值。
(condition) ? isTruthy : isFalsy
If/Else 的替代方案
让我们开始一个场景并通过不同的解决方案的例子来走一遍。
我们将从用户输入中取颜色,并需要将它们转换为一些预设的颜色代码以匹配,这样我们就可以改变我们的背景颜色。所以我们将检查颜色名称的字符串并设置我们的颜色代码,如果我们有匹配。
const setBackgroundColor = (colorName) => {
let colorCode = "";
if (colorName === "blue") {
colorCode = "#2196F3";
} else if (colorName === "green") {
colorCode = "#4CAF50";
} else if (colorName === "orange") {
colorCode = "#FF9800";
} else if (colorName === "pink") {
colorCode = "#E91E63";
} else {
colorCode = "#F44336";
}
document.body.style.backgroundColor = colorCode;
};
这个 if/else 完成了任务。但我们被大量重复的逻辑所拖累,比较 colorName 和重复分配 colorCode。
Switch
现在我们可以更恰当地将这个转换成一个 switch 语句。它更符合我们想要做的概念;我们有几个字符串案例我们想匹配,如果没有我们的案例匹配,则默认。
const setBackgroundColor = (colorName) => {
let colorCode = "";
switch (colorName) {
case "blue":
colorCode = "#2196F3";
break;
case "green":
colorCode = "#4CAF50";
break;
case "orange":
colorCode = "#FF9800";
break;
case "pink":
colorCode = "#E91E63";
break;
default:
colorCode = "#f44336";
}
document.body.style.backgroundColor = colorCode;
};
但是 switch 仍然带来了我们可以没有的大量样板和重复代码。
查找表
那么我们真正想要实现什么呢?我们需要将颜色名称的十六进制颜色代码分配,所以让我们创建一个对象,该对象将颜色名称作为键,颜色代码作为值。然后我们可以通过使用 object[key] 来查找我们的颜色代码。我们需要一个默认值,所以一个简短的三元运算符,如果没有找到键则返回默认值,这样做的同时使默认部分成为我们对象的一部分。
const colorCodes = {
blue: "#2196F3",
green: "#4CAF50",
orange: "#FF9800",
pink: "#E91E63",
default: "#F44336",
};
const setBackgroundColor = (colorName) => {
document.body.style.backgroundColor = colorCodes[colorName]
? colorCodes[colorName]
: colorCodes["default"];
};
现在我们有了一个查找表,它整齐地列出了我们的输入和可能的输出。
这不是关于奇迹般的“代码行数”(LOC)减少(我们从 15 减到 20 再减到 12。)事实上,这些解决方案可能会增加你的 LOC,但我们增加了可维护性、可读性,并实际上通过只有一个逻辑检查作为默认回退减少了复杂性。
用数据交换逻辑
使用查找表而不是 if/else 或 switch 的最重要成就是,我们将多个比较逻辑实例转换为数据。代码更具表现力;它将逻辑显示为一个操作。代码更可测试;逻辑已经减少。而且我们的比较更易于维护;它们作为纯数据被整合了。
让我们将五个比较逻辑操作减少到一个,并将我们的值转换为数据。
场景:我们需要将成绩百分比转换为它们的字母成绩等价物。
一个 if/else 足够简单;我们从上到下检查成绩是否高于或等于匹配字母成绩所需的。
const getLetterGrade = (gradeAsPercent) => {
if ((gradeAsPercent = 90)) {
return "A";
} else if (gradeAsPercent >= 80) {
return "B";
} else if (gradeAsPercent >= 70) {
return "C";
} else if (gradeAsPercent >= 60) {
return "D";
} else {
return "F";
}
};
但我们一次又一次地重复同样的逻辑操作。
所以让我们把我们的数据提取到一个数组中(以保持顺序)并将每个成绩可能性表示为一个对象。现在我们只需要对我们的对象做一个 >=
比较并找到我们数组中的第一个匹配项。
const gradeChart = [
{ minpercent: 90, letter: "A" },
{ minpercent: 80, letter: "B" },
{ minpercent: 70, letter: "C" },
{ minpercent: 60, letter: "D" },
{ minpercent: 0, letter: "F" },
];
const getLetterGrade = (gradeAsPercent) => {
const grade = gradeChart.find((grade) => gradeAsPercent >= grade.minpercent);
return grade.letter;
};
开始将你的比较想象为数据
当你需要比较或“检查”值时,自然会想到使用 if/else
,这样你就可以用言语走过问题。但下一次尝试想象你的值如何可以被表示为数据,你的逻辑减少到解释那些数据。
你的代码将最终变得更可读、更易于维护,并且在其意图中更加有目的性,清晰地分离了它所代表的概念。这里所有的代码示例都是可行的,但正确的方法可以将 It Works™ 代码转变为“很棒的工作”代码。
反思
此篇文章讨论了过度使用 if/else
语句的问题,并提出了几种替代方案来优化代码结构和可维护性。在直译中,大部分内容已经相对准确地转换为中文,但仍有几处需要注意:
- 一些表达方式并不完全贴合中文习惯,例如“斯巴达代码”(spaghetti code)直译可能不易于理解,需要找到更合适的表达。
- 部分专业术语的翻译需要保留原文,以避免混淆,如“lookup table”直译为“查找表”,但为了清晰应再次确认其原英文术语。
- 示例代码和一些专有名词(如颜色名称、HTML 属性)保留了原文,这是符合规则的。
- 对于“ternary”(三元运算符)的介绍部分,可能需要更简洁的解释,以适应非专业读者的理解水平。