寫程式時該用三元運算子 (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 語言則直接表示,因為三元運算子容易造成難以閱讀的程式碼,所以決定不支援這種語法。
總的來說,程式碼是種表達與溝通的工具,三元運算子雖然在某些狀況比較簡潔,但確實有時反而會導致程式碼比較難閱讀。回過頭來說,一個工具的使用,還是端看使用的人怎麼用;不要教條式的認為三元運算子比較簡潔,或者比較難閱讀,而是要看情況,在適合的情況使用,達到最好的效果。