今天來介紹我個人很常用的小撇步,關於 OR (||
) 與 AND (&&
),除了很單純用來判斷回傳布林值,同時也可以拿來賦值哦!
但同時,這種方式也很容易因為意想不到的 truthy、falsy value,造成不如預期的結果,需要特別小心。
今天也會講到與其息息相關的 Short Circuit Evaluation,可以大幅縮短判斷式的長度,提高可讀性。
今天就來看看,如何應用它們,以及實戰容易踩到的地雷。
大概可以直翻成「短路取值」,好像應該先來了解一下「短路」:
最上面是電池,S 是開關,L 是燈泡,S 沒有連通的情況下,電流只能夠走 L;但當 S 接通了,電流可以選擇走 S 或 L,肯定會選電阻比較小的 S,就會造成短路。
沒錯,物理課開始囉!想不到物理知識也可以應用在寫程式呢!
說到短路,大部分人抱持負面印象,因為短路可能會造成火災,或者造成電器損壞,甚至還有「腦袋短路」這種很有趣的說法。
但事實上,如果今天站在一個電子的立場來看,短路才是最正常的啊!因為短路其實就像字面上的意思,要走路的當然挑「短」的路,沒事讓自己這麼累幹嘛?但短的路不一定輕鬆,所以更正確來說,是要找「電阻最小」的路。
那如果套用到寫程式,要怎麼找到「阻礙最小」、「最輕鬆」的路呢?
那當然是讓「運算愈少愈好」!
OR 運算子 (||
) 在一般的理解中,就是當「或」的概念在使用:
const hasVIPcard = false;
const money = 1500;
if (money > 1000 || hasVIPcard) {
console.log('尊榮會員');
}
比如上面這個例子,如果 money
大於 1000 或 hasVIPcard
為 true
,任何一個成立都可以是尊榮會員。
但是對於程式來說,既然「只要任何一個成立都可以」,那我當然走短路啊!
由左至右,任何一個判斷式為
true
,就回傳true
,後面就不執行了。
因此,上面的程式其實完全沒有去讀 hasVIPcard
的值,就已經進入第 4 行跑 console.log
了。
怎麼知道的呢?我們稍微改一下這個範例:
const isUserHasVipCard = () => {
console.log('是否有 VIP card?');
return false;
};
const money = 1500;
if (money > 1000 || isUserHasVipCard()) {
console.log('尊榮會員');
}
執行結果
尊榮會員
可以看到 isUserHasVipCard
這個函式完全沒有進去,因為程式走「短路」,第一個判斷式就知道答案了,何必再去執行第二個?
應用上述的原理,其實 OR 運算子不只能夠用在判斷式,也能夠用在賦值(取值):
const fruit = 'orange' || 'apple';
const fruit2 = '' || 'apple';
console.log(fruit);
console.log(fruit2);
執行結果
orange
apple
很神奇吧!我們平常使用 ||
都是拿來「判斷」,都是回傳 boolean,結果這邊居然可以回傳字串!
原理其實跟上面的短路概念是類似的,挑最輕鬆的路走:
const result = a || b
如果 a
是 truthy value,就直接回傳 a
,若否則回傳 b
。相當於:
let result;
if (a) {
result = a;
} else {
result = b;
}
const result = a || b || c
如果 a
是 truthy value,就直接回傳 a
,若否則看 b
是否為 truthy value,若否則回傳 c
。相當於:
let result;
if (a) {
result = a;
} else if (b) {
result = b;
} else {
result = c;
}
想像你是個想要早點打卡下班的電腦程式,當然能少走一個 if 是一個啊!
有了上述的基礎,我們對於「||
」有了全新的認識,透過 OR 的短路取值,經常被用在當作 default 值:
const arr = [
{ name: 'washing machine', price: 8000 },
{ name: 'TV' },
{ name: 'laptop', price: 25000 },
];
arr.reduce((sum, item) => {
const price = item.price || 0;
return sum + price;
}, 0);
執行結果
33000
第 8 行如果少了那個關鍵的 || 0
,跑出來的結果會直接變 NaN
哦!
如果今天網頁上有個重點廣告區:
const arr = [
{ name: 'washing machine', price: 8000, vip: false },
{ name: 'TV', price: 13500, vip: false },
{ name: 'laptop', price: 25000, vip: false },
];
const advertisement =
arr.filter(item => item.vip)[0] ||
arr.filter(item => item.price > 10000)[0] ||
arr[0];
console.log(advertisement.name);
執行結果
TV
以上是關於 OR 短路取值的應用,但使用這招時(尤其是當預設值時),需要特別注意這種「長得像 falsy 的 truthy」:
const fruitList = [] || ['apple', 'orange', 'banana'];
執行結果
[]
And 運算子 (&&
) 在一般的理解中,就是當「而且」的概念在使用:
const hasVIPcard = true;
const money = 500;
if (money > 1000 && hasVIPcard) {
console.log('尊榮又有錢的會員');
}
比如上面這個例子,如果 money
大於 1000 而且 hasVIPcard
為 true
,兩個都要成立才會是尊榮又有錢的會員。
但是對於程式來說,既然「只要任何一個不成立就不是」,那我當然走短路啊!
由左至右,任何一個判斷式為
false
,就回傳false
,後面就不執行了。
因此,上面的程式其實完全沒有去讀 hasVIPcard
的值,就已經進入第 4 行跑 console.log
了。
怎麼知道的呢?我們稍微改一下這個範例:
const isUserHasVipCard = () => {
console.log('是否有 VIP card?');
return true;
};
const money = 500;
if (money > 1000 && isUserHasVipCard()) {
console.log('尊榮又有錢的會員');
}
執行結果
可以看到 isUserHasVipCard
這個函式完全沒有進去,因為程式走「短路」,第一個判斷式就知道答案了,何必再去執行第二個?
應用上述的原理,其實 And 運算子不只能夠用在判斷式,也能夠用在賦值(取值):
const fruit = 'orange' && 'apple';
const fruit2 = '' && 'apple';
console.log(fruit);
console.log(fruit2);
執行結果
apple
很神奇吧!我們平常使用 &&
都是拿來「判斷」,都是回傳 boolean,結果這邊居然可以回傳字串!
原理其實跟上面的短路概念是類似的,挑最輕鬆的路走:
const result = a && b
如果 a
是 falsy value,就直接回傳 a
,若否則回傳 b
。相當於:
let result;
if (!a) {
result = a;
} else {
result = b;
}
const result = a && b && c
如果 a
是 falsy value,就直接回傳 a
,若否則看 b
是否為 falsy value,若否則回傳 c
。相當於:
let result;
if (!a) {
result = a;
} else if (!b) {
result = b;
} else {
result = c;
}
比起上面 OR 的版本,就只是多了一個驚嘆號,將 boolean 反轉而已!
有了上述的基礎,我們對於「&&
」有了全新的認識,透過 AND 的短路取值,可以用來快速判斷一個有很多層的(nested) object,取得指定的深層 property。
比如以下範例會出錯:
const personList = [
{
height: 173,
weight: 63,
car: {
color: 'white',
price: 500000
}
},
{
height: 163,
weight: 55
}
];
personList.forEach(person => {
const carPrice = person.car.price;
console.log(carPrice);
});
執行結果
500000
Uncaught TypeError: Cannot read property 'price' of undefined
主要是因為有些 person
是沒有 car
的,因此如果硬是取 price
有可能會出錯。
可以改成用 &&
來協助取值:
const personList = [
{
height: 173,
weight: 63,
car: {
color: 'white',
price: 500000
}
},
{
height: 163,
weight: 55
}
];
personList.forEach(person => {
const carPrice = person && person.car && person.car.price;
if (carPrice) {
console.log(carPrice);
}
});
執行結果
500000
這樣寫,會在判斷到 person.car
這個 falsy value 時,直接回傳 undefined
,就不會繼續找 person.car.price
了。
當然這樣寫起來是非常不美觀的,如果只有一兩層還可以這樣寫,若真的要很多層,也可以考慮使用第三方套件(如:lodash/get
)來代替。
今天介紹了大家原本應該很熟的 OR 與 AND,原來它們不只可以透過短路取值來提升效率,甚至還可以用這個方式,做到原本需要一堆 if else
才做得到的事情!
當然這中間是需要相當細心的,因為短路取值的判斷根本,是用昨天討論到的 truthy/falsy value,所以如果對於這兩個概念不熟悉,很容易就會在短路取值上撞牆,務必好好學習這兩天的內容,未來寫的程式可以簡化許多唷!
在真與假之中
拼湊未來的模樣
確認 property path 取值的時候也可以考慮使用 Optional chaining
const customerCity = customer.details?.address?.city;
這超方便!我之前專案還沒有支援這個,常常需要靠 lodash 先做確認,算滿麻煩的,好險 JavaScript 一直在進步!