2022 年 6 月 5 日

邏輯運算子

JavaScript 中有四個邏輯運算子:|| (OR)、&& (AND)、! (NOT)、?? (Nullish Coalescing)。這裡我們將介紹前三個,?? 運算子將在下一篇文章中介紹。

儘管它們被稱為「邏輯」,但它們可以應用於任何類型的值,而不僅僅是布林值。它們的結果也可以是任何類型。

讓我們看看詳細資訊。

|| (OR)

「OR」運算子用兩個垂直線符號表示

result = a || b;

在傳統程式設計中,邏輯 OR 僅用於處理布林值。如果它的任何參數為 true,它會傳回 true,否則會傳回 false

在 JavaScript 中,運算子更為複雜且更強大。但首先,讓我們看看布林值會發生什麼事。

有四種可能的邏輯組合

alert( true || true );   // true
alert( false || true );  // true
alert( true || false );  // true
alert( false || false ); // false

正如我們所見,結果總是 true,除了兩個運算元都是 false 的情況。

如果運算元不是布林值,它會在評估時轉換為布林值。

例如,數字 1 被視為 true,數字 0 被視為 false

if (1 || 0) { // works just like if( true || false )
  alert( 'truthy!' );
}

大多數時候,OR || 用於 if 敘述中,以測試給定條件中是否有 任何 條件為 true

例如

let hour = 9;

if (hour < 10 || hour > 18) {
  alert( 'The office is closed.' );
}

我們可以傳遞更多條件

let hour = 12;
let isWeekend = true;

if (hour < 10 || hour > 18 || isWeekend) {
  alert( 'The office is closed.' ); // it is the weekend
}

OR "||" 找出第一個真值

上述描述的邏輯有點經典。現在,讓我們引入 JavaScript 的「額外」功能。

延伸演算法的工作方式如下。

給定多個 OR 值

result = value1 || value2 || value3;

OR || 運算子執行下列動作

  • 從左到右評估運算元。
  • 對於每個運算元,將其轉換為布林值。如果結果為 true,則停止並傳回該運算元的原始值。
  • 如果所有運算元都已評估(即全部為 false),則傳回最後一個運算元。

值以其原始形式傳回,不進行轉換。

換句話說,OR || 鏈會傳回第一個真值,或在找不到真值時傳回最後一個真值。

例如

alert( 1 || 0 ); // 1 (1 is truthy)

alert( null || 1 ); // 1 (1 is the first truthy value)
alert( null || 0 || 1 ); // 1 (the first truthy value)

alert( undefined || null || 0 ); // 0 (all falsy, returns the last value)

與「純粹、經典、僅布林值的 OR」相比,這會導致一些有趣的用法。

  1. 從變數或表達式清單中取得第一個真值。

    例如,我們有 firstNamelastNamenickName 變數,全部都是選用的(即可以是未定義或具有假值)。

    讓我們使用 OR || 選擇具有資料的那個並顯示它(或在未設定任何內容時顯示 "Anonymous"

    let firstName = "";
    let lastName = "";
    let nickName = "SuperCoder";
    
    alert( firstName || lastName || nickName || "Anonymous"); // SuperCoder

    如果所有變數都是假值,則會顯示 "Anonymous"

  2. 短路評估。

    OR || 運算子的另一個特點是所謂的「短路」評估。

    這表示 || 會處理其參數,直到達到第一個真值,然後立即傳回該值,甚至不會觸及其他參數。

    如果運算元不只是值,而是具有副作用的表達式(例如變數指定或函式呼叫),則此功能的重要性就顯而易見。

    在以下範例中,只會列印第二個訊息

    true || alert("not printed");
    false || alert("printed");

    在第一行中,OR || 運算子在看到 true 時會立即停止評估,因此不會執行 alert

    有時,人們會使用此功能來執行命令,前提是左邊條件為假。

&& (AND)

AND 運算子用兩個連字號 && 表示

result = a && b;

在傳統程式設計中,如果兩個運算元都為真,AND 會傳回 true,否則傳回 false

alert( true && true );   // true
alert( false && true );  // false
alert( true && false );  // false
alert( false && false ); // false

使用 if 的範例

let hour = 12;
let minute = 30;

if (hour == 12 && minute == 30) {
  alert( 'The time is 12:30' );
}

就像 OR 一樣,任何值都可以作為 AND 的運算元

if (1 && 0) { // evaluated as true && false
  alert( "won't work, because the result is falsy" );
}

AND “&&” 會找出第一個假值

給定多個 AND 值

result = value1 && value2 && value3;

AND && 運算子會執行下列動作

  • 從左到右評估運算元。
  • 針對每個運算元,將其轉換為布林值。如果結果為 false,則停止並傳回該運算元的原始值。
  • 如果所有運算元都已評估(即所有運算元都為真),則傳回最後一個運算元。

換句話說,AND 會傳回第一個假值,或者如果沒有找到假值,則傳回最後一個值。

上述規則類似於 OR。不同之處在於,AND 傳回第一個值,而 OR 傳回第一個值。

範例

// if the first operand is truthy,
// AND returns the second operand:
alert( 1 && 0 ); // 0
alert( 1 && 5 ); // 5

// if the first operand is falsy,
// AND returns it. The second operand is ignored
alert( null && 5 ); // null
alert( 0 && "no matter what" ); // 0

我們也可以連續傳遞多個值。看看第一個假值是如何傳回的

alert( 1 && 2 && null && 3 ); // null

當所有值都為真時,會傳回最後一個值

alert( 1 && 2 && 3 ); // 3, the last one
AND && 的優先權高於 OR ||

AND && 運算子的優先權高於 OR ||

因此,程式碼 a && b || c && d 基本上與 && 運算式放在括號中相同:(a && b) || (c && d)

不要用 ||&& 取代 if

有時,人們會使用 AND && 運算子作為「撰寫 if 的較短方式」。

例如

let x = 1;

(x > 0) && alert( 'Greater than zero!' );

&& 右邊的動作只會在評估到達時執行。也就是說,只有當 (x > 0) 為真時才會執行。

因此,我們基本上有一個類比

let x = 1;

if (x > 0) alert( 'Greater than zero!' );

儘管使用 && 的變體看起來較短,但 if 較為明顯,且傾向於更易於閱讀。因此,我們建議針對其目的使用每個結構:如果我們想要 if,就使用 if;如果我們想要 AND,就使用 &&

!(非)

布林非運算子以驚嘆號 表示。

語法非常簡單

result = !value;

運算子接受單一參數並執行下列動作

  1. 將運算元轉換為布林類型:true/false
  2. 傳回反向值。

例如

alert( !true ); // false
alert( !0 ); // true

有時會使用雙非 !! 來將值轉換為布林類型

alert( !!"non-empty string" ); // true
alert( !!null ); // false

也就是說,第一個非將值轉換為布林並傳回反向值,而第二個非再次反向值。最後,我們得到一個純粹的值轉布林轉換。

有一個稍微冗長的作法可以執行相同的事情,也就是內建的 布林 函式

alert( Boolean("non-empty string") ); // true
alert( Boolean(null) ); // false

的優先權高於所有邏輯運算子,因此它總是會先執行,在 &&|| 之前。

任務

重要性:5

下列程式碼會輸出什麼?

alert( null || 2 || undefined );

答案是 2,這是第一個真值。

alert( null || 2 || undefined );
重要性:3

下列程式碼會輸出什麼?

alert( alert(1) || 2 || alert(3) );

答案:首先是 1,然後是 2

alert( alert(1) || 2 || alert(3) );

呼叫 alert 沒有傳回值。換句話說,它傳回 undefined

  1. 第一個 OR || 評估其左運算元 alert(1)。這會顯示第一則訊息 1
  2. alert 傳回 undefined,因此 OR 會繼續執行第二個運算元,尋找真值。
  3. 第二個運算元 2 為真值,因此執行會停止,傳回 2,然後由外部警示顯示。

不會有 3,因為評估不會執行到 alert(3)

重要性:5

這段程式碼會顯示什麼?

alert( 1 && null && 2 );

答案:null,因為它是清單中的第一個假值。

alert(1 && null && 2);
重要性:3

這段程式碼會顯示什麼?

alert( alert(1) && alert(2) );

答案:1,然後是 undefined

alert( alert(1) && alert(2) );

呼叫 alert 傳回 undefined(它只會顯示訊息,因此沒有有意義的傳回值)。

因此,&& 會評估左運算元(輸出 1),然後立即停止,因為 undefined 是假值。而 && 會尋找假值並傳回,因此它已經完成了。

重要性:5

結果會是什麼?

alert( null || 2 && 3 || 4 );

答案:3

alert( null || 2 && 3 || 4 );

AND && 的優先權高於 ||,因此它會先執行。

2 && 3 = 3 的結果,因此表達式會變成

null || 3 || 4

現在結果為第一個真值:3

重要性:3

寫一個 if 條件來檢查 age 是否介於 1490(包含)。

「包含」表示 age 可以達到邊界 1490

if (age >= 14 && age <= 90)
重要性:3

寫一個 if 條件來檢查 age 是否不在 1490(包含)之間。

建立兩個變體:第一個使用 NOT !,第二個不使用。

第一個變體

if (!(age >= 14 && age <= 90))

第二個變體

if (age < 14 || age > 90)
重要性:5

下列哪個 alert 會執行?

if(...) 內的表達式結果會是什麼?

if (-1 || 0) alert( 'first' );
if (-1 && 0) alert( 'second' );
if (null || -1 && 1) alert( 'third' );

答案:第一個和第三個會執行。

詳細說明

// Runs.
// The result of -1 || 0 = -1, truthy
if (-1 || 0) alert( 'first' );

// Doesn't run
// -1 && 0 = 0, falsy
if (-1 && 0) alert( 'second' );

// Executes
// Operator && has a higher precedence than ||
// so -1 && 1 executes first, giving us the chain:
// null || -1 && 1  ->  null || 1  ->  1
if (null || -1 && 1) alert( 'third' );
重要性:3

寫一個使用 prompt 詢問登入的程式碼。

如果訪客輸入 "Admin",則提示輸入密碼;如果輸入為空行或 Esc,則顯示「已取消」;如果輸入為其他字串,則顯示「我不認識你」。

密碼檢查如下

  • 如果等於「TheMaster」,則顯示「歡迎!」,
  • 其他字串,則顯示「密碼錯誤」,
  • 對於空字串或取消輸入,則顯示「已取消」

架構

請使用巢狀 if 程式區塊。注意程式碼的整體可讀性。

提示:傳遞空輸入至提示會傳回空字串 ''。在提示期間按 ESC 會傳回 null

執行示範

let userName = prompt("Who's there?", '');

if (userName === 'Admin') {

  let pass = prompt('Password?', '');

  if (pass === 'TheMaster') {
    alert( 'Welcome!' );
  } else if (pass === '' || pass === null) {
    alert( 'Canceled' );
  } else {
    alert( 'Wrong password' );
  }

} else if (userName === '' || userName === null) {
  alert( 'Canceled' );
} else {
  alert( "I don't know you" );
}

注意 if 程式區塊內的垂直縮排。技術上來說,這些縮排不是必需的,但可以讓程式碼更易於閱讀。

教學地圖

留言

留言前請先閱讀…
  • 如果你有改善建議,請 提交 GitHub 議題 或拉取請求,而不是留言。
  • 如果您無法理解文章中的某個部分,請詳細說明。
  • 若要插入一些程式碼,請使用 <code> 標籤,對於多行程式碼,請將它們包覆在 <pre> 標籤中,對於超過 10 行的程式碼,請使用沙盒 (plnkrjsbincodepen…)