写程式时该用三元运算子 (ternary operator) 吗?
2024年5月27日
前阵子看到一个在讨论三元运算子 (ternary operator) 的讨论串,觉得蛮有趣的。因此在这期双周报,来跟大家谈这个运算子。以前端开发来说,三元运算子是蛮常用到的,但社群的人对使用三元运算子有不同的观点。
先来看看 MDN 上怎么定义三元运算子,MDN 的定义是“运算子是 JavaScript 唯一用到三个运算元的运算子:在一个条件后面会跟着一个问号(?)
,如果条件是 truthy,在冒号(:)
前的表达式会被执行,如果条件是 falsy ,在冒号后面的表达式会被执行,这个运算子常常被用来当作if
的简洁写法”。
举个具体的例子,如果今天有一个 getFee
函式,会根据是不是会员,来回传不同的价格。如果是会员,则需要 $2 块钱,不是会员则需要 $10 块钱。我们可以简单地写成:
function getFee(isMember) {
if (isMember) {
return "$2.00";
} else {
return "$10.00";
}
}
如果用三元运算子,则可以变成这样,原本四行的程式码,被一行简化:
function getFee(isMember) {
return isMember ? "$2.00" : "$10.00";
}
虽说上面的例子,三元运算子看起来比较简洁,但在一些不同的情况下,三元运算子反而会让可读性变差,这也是为什么 ESLint 当中甚至有条 no-nested-ternary
,禁止下面这种看过去让人没办法一眼看懂的程式码:
foo ? (baz === qux ? quxx() : foobar()) : bar();
三元运算子究竟好不好用,其实很主观。举例来说,有人认为三元运算子会比用 &&
来的好,举例来说,下面这种状况,是在写前端 JSX 元件很常会出现的问题,因为 members.length
如果是 0
的话, 0 && 某个东西
结果会直接渲染 0
。
<div>
{members.length &&
members.map((member) => <div key={member.id}> {member.name} </div>)}
</div>
因此有些人会认为,这时候用三元运算子比较好 (例如下面这样)。但有人则认为三元运算子的可读性会比较差,所以认为与其用三元运算子,不如确定 &&
前面会是布林值,例如检查 members.length > 0
而不是检查 members.length
。
<div>
{members.length
? members.map((member) => <div key={member.id}> {member.name} </div>)
: null}
</div>
另外则有人说,与其用三元运算子,不如把这个抽成元件,然后在元件中引用,这样的可读性又更高一点。
function MemberList({ members }: MemberListProps) {
if (members.length === 0) {
return null;
}
return (
<div>
{members.map((member) => (
<div key={member.id}> {member.name} </div>
))}
</div>
);
}
不只在 JavaScript 中,三元运算子有争议;在其他的程式语言中,三元运算子也有被大量讨论过。JavaScript 的三元运算子语法是跟 C 比较类似的,是由 ?
与 :
所组成。
然而这种设计,有时看起来过于抽象,难以让人一眼看懂,所以在其他的语言中,会看到不同的设计方式。举例来说,Python 是把 if...else
直接化为一行的形式 '$2.00' if isMember else '$10.00'
。而 Go 语言则直接表示,因为三元运算子容易造成难以阅读的程式码,所以决定不支援这种语法。
总的来说,程式码是种表达与沟通的工具,三元运算子虽然在某些状况比较简洁,但确实有时反而会导致程式码比较难阅读。回过头来说,一个工具的使用,还是端看使用的人怎么用;不要教条式的认为三元运算子比较简洁,或者比较难阅读,而是要看情况,在适合的情况使用,达到最好的效果。