ㄟ問你喔,邏輯運算子 && 和 || 到底該怎麼用?
JavaScript 有各種運算子,賦值運算子、比較運算子、算術運算子…,而本篇重點是在理解邏輯運算子的運算過程。
初見 && 、 || 、 ! 時,內心真的翻了個白眼,只有「天啊這到底是哪來的外星語」之感,因此本文將竭盡能事的把邏輯運算子轉譯為人能聽得懂的話,幾個重點將有:
- 邏輯運算子簡介
- 短路求值 (Short-circuit evaluation)
- 邏輯運算子轉換布林值:回傳 true、false
- 邏輯運算子轉換非布林值:回傳真值(truthy value)、虛值(falsy value)
- 真值與虛值列表大集合
- 邏輯運算子的權重
邏輯運算子簡介
有三種符號,以下同步翻譯為人話:
- && ( 人話的 and ):當所有用 && 連接的條件都是 true,就回傳 true,否則回傳 false
- || ( 人話的 or ):所有用 || 連接的條件,只要其中有一個是 true,就回傳 true
- ! ( 人話的 not ):!true 會回傳 false,而 !false 會回傳 true
短路求值 (short-circuit evaluation)
根據 MDN 文件中是這樣定義的:
邏輯運算子 通常被用於布林(邏輯)值; 使用於 布林(邏輯)值時,它們會回傳布林型態的值。然而,
&&
和||
運算子實際上是回傳兩指定運算元之一,因此用於非布林型態值時,它可能會回傳一個非布林型態的值。
而上述這段話大概又可以再拆解出下面這兩個問題:
1. 什麼叫做短路求值?
2. 布林值、真值、虛值到底怎麼分?
何謂短路求值?
根據 MDN 的解釋:
Short-circuit evaluation
As logical expressions are evaluated left to right, they are tested for possible “short-circuit” evaluation using the following rules:
false && anything
is short-circuit evaluated to false.
true || anything
is short-circuit evaluated to true.The rules of logic guarantee that these evaluations are always correct. Note that the anything part of the above expressions is not evaluated, so any side effects of doing so do not take effect.
也就是說:
1. 邏輯運算子的運算順序是:由左至右。
2. 在 && 的運算中,可求出為 false 的值。
3. 在 || 的運算中,可求出為 true 的值。
&& 邏輯運算子的短路求值
還是一樣舉例來說明:
從 MDN 已經知道定義了。但假如有 2 個以上的布林值用 && 運算呢?這樣看可能會更清楚。
從以下四個印出的結果,可以想像,&& 的運算過程只要遇到 false 就跳出停止,同時得到 false。
&& 的運算
console.log(true && true); //回傳 true
console.log(false && true); //回傳 false
console.log(true && false && true); //回傳 false
console.log(true && true && false && true); //回傳 false
|| 邏輯運算子的短路求值
|| 則是在遇到都是 false 的情況下,才回傳 false;反之,只要有一個 true 就會回傳 true。
但假如有 2 個以上的布林值用 || 運算呢?那結果又會如何?
從以下印出的結果,一樣可以想像為,|| 的運算過程只要遇到 true 就跳出停止,同時得到 true 的結果。
|| 的運算
console.log(true || true); //回傳 true
console.log(false || false); //回傳 false
console.log(true || false); //回傳 true
console.log(false || true || false); //回傳 true (在第二個true跳出)
console.log(false && false && true && true); // true(在第三個true跳出)
看到這邊,大概可以歸納出何謂「短路賦值」了,也就是:
邏輯運算子 && 的運算過程:
會是由左至右,回傳第一個是 falsy 的值。若皆為 truthy,則回傳最後一個值。邏輯運算子 || 的運算過程:
會是由左至右,回傳第一個是 truthy 的值。若皆為 falsy,則回傳最後一個值。
以上的部分,都是邏輯運算子的布林值運算,但如果要運算非布林值的時候,又該如何判斷 true 或 false?
邏輯運算子轉換「布林值」:回傳 true、false
首先還是先回顧一下,依照最基礎定義,&&
和 ||
在運算「純」布林值的時候,最基礎可發展出這八種變化:
console.log(true && true) // 回傳 true
console.log(true && false) // 回傳 false
console.log(false && true) // 回傳 false
console.log(false && false) // 回傳 falseconsole.log(true || true) // 回傳 true
console.log(true || false) // 回傳 true
console.log(false || true) // 回傳 true
console.log(false || false) // 回傳 false
邏輯運算子轉換非布林值:回傳真值、虛值
邏輯運算子也同時能用於運算「非布林值」,而這些「非布林值」當然就包括原始型別與物件型別:
數字
console.log(0 && 1)//回傳值為 0
console.log(0 || 1)//回傳值為 1字串
console.log('goodbye' && 'hello') //回傳 'hello'
console.log('goodbye' || 'hello') //回傳 'goodbye'null 及 undefined
console.log(null && undefined) // 回傳 null
console.log(null || undefined) // 回傳 undefined物件 及 陣列
console.log({} && []) // 回傳 array
console.log({} || []) // 回傳 object
如果你看完邏輯運算子轉換非布林值的過程後,也跟我一樣很想問,為什麼會這樣?那麼在這邊就應該先了解一下何謂真值與虛值了(上面8題詳解在下方)。
真值(Truthy Value)與虛值(Falsy Value)列表大集合
真值、虛值與布林值到底該怎麼分?
請先記得:
- 布林值 true 和 false 是原始資料型別的其中一種。
- truthy 不等於 true、falsy 也不等於 false,它們是不同的東西。
- falsy 以外的都是 truthy
原始資料來源為深入理解 TypeScript,再另將表格整理後製表。
看完 truthy、falsy 之後,應該能比較好理解邏輯運算子是如何運算非布林值的了吧!那麼讓我們再一次回顧剛剛的8題:
數字
console.log(0 && 1)//回傳值為 0,為 falsy
console.log(0 || 1)//回傳值為 1,為 truthy字串
console.log('goodbye' && 'hello') //回傳 'hello',字串是 truthy
console.log('goodbye' || 'hello') //回傳 'goodbye',字串是 truthynull 及 undefined
console.log(null && undefined) // 回傳 null,是 falsy
console.log(null || undefined) // 回傳 undefined,是 falsy物件 及 陣列
console.log({} && []) // 回傳 array,陣列是truthy
console.log({} || []) // 回傳 object,物件是truthy
希望看到這邊,你已經更理解邏輯運算子的運算過程了,但是,你知道嗎?其實邏輯運算子也跟 CSS 的 class、ID 一樣,有權重之分哪!
邏輯運算子的權重
有關邏輯運算子的權重,可以參考這篇 MDN。
根據 MDN 的資料來看,&& 的權重為 6 分、|| 的權重為 5 分,因此當遇到 && 與 || 在同一個運算式的時候,就會先算 && 再算 ||。
console.log(null || NaN && "hello" && 666 || "0")
在這個例子當中,電腦會先運算 NaN && “hello” && 666 這一段,再處理其餘的部分,因此回傳結果為 “0”。
你,答對了嗎?
參考資料