Javascript 邏輯運算符

2023-02-17 10:37 更新

JavaScript 中有四個邏輯運算符:?||?(或),?&&?(與),?!?(非),????(空值合并運算符)。本文我們先介紹前三個,在下一篇文章中再詳細介紹 ???? 運算符。

雖然它們被稱為“邏輯”運算符,但這些運算符卻可以被應用于任意類型的值,而不僅僅是布爾值。它們的結果也同樣可以是任意類型。

讓我們來詳細看一下。

||(或)

兩個豎線符號表示“或”運算符:

result = a || b;

在傳統(tǒng)的編程中,邏輯或僅能夠操作布爾值。如果參與運算的任意一個參數(shù)為 true,返回的結果就為 true,否則返回 false。

在 JavaScript 中,邏輯運算符更加靈活強大。但是,首先讓我們看一下操作數(shù)是布爾值的時候發(fā)生了什么。

下面是四種可能的邏輯組合:

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

正如我們所見,除了兩個操作數(shù)都是 false 的情況,結果都是 true。

如果操作數(shù)不是布爾值,那么它將會被轉化為布爾值來參與運算。

例如,數(shù)字 1 被作為 true 處理,數(shù)字 0 則被作為 false

if (1 || 0) { // 工作原理相當于 if( true || false )
  alert( 'truthy!' );
}

大多數(shù)情況下,邏輯或 || 會被用在 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.' ); // 是周末
}

或運算尋找第一個真值

上文提到的邏輯處理多少有些傳統(tǒng)了。下面讓我們看看 JavaScript 的“附加”特性。

拓展的算法如下所示。

給定多個參與或運算的值:

result = value1 || value2 || value3;

或運算符 || 做了如下的事情:

  • 從左到右依次計算操作數(shù)。
  • 處理每一個操作數(shù)時,都將其轉化為布爾值。如果結果是 ?true?,就停止計算,返回這個操作數(shù)的初始值。
  • 如果所有的操作數(shù)都被計算過(也就是,轉換結果都是 ?false?),則返回最后一個操作數(shù)。

返回的值是操作數(shù)的初始形式,不會做布爾轉換。

換句話說,一個或運算 ?||? 的鏈,將返回第一個真值,如果不存在真值,就返回該鏈的最后一個值。

例如:

alert( 1 || 0 ); // 1(1 是真值)

alert( null || 1 ); // 1(1 是第一個真值)
alert( null || 0 || 1 ); // 1(第一個真值)

alert( undefined || null || 0 ); // 0(都是假值,返回最后一個值)

與“純粹的、傳統(tǒng)的、僅僅處理布爾值的或運算”相比,這個規(guī)則就引起了一些很有趣的用法。

  1. 獲取變量列表或者表達式中的第一個真值。
  2. 例如,我們有變量 firstName、lastName 和 nickName,都是可選的(即可以是 undefined,也可以是假值)。

    我們用或運算 || 來選擇有數(shù)據(jù)的那一個,并顯示出來(如果沒有設置,則用 "Anonymous"):

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

    如果所有變量的值都為假,結果就是 "Anonymous"。

  3. 短路求值(Short-circuit evaluation)。
  4. 或運算符 || 的另一個用途是所謂的“短路求值”。

    這指的是,|| 對其參數(shù)進行處理,直到達到第一個真值,然后立即返回該值,而無需處理其他參數(shù)。

    如果操作數(shù)不僅僅是一個值,而是一個有副作用的表達式,例如變量賦值或函數(shù)調(diào)用,那么這一特性的重要性就變得顯而易見了。

    在下面這個例子中,只會打印第二條信息:

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

    在第一行中,或運算符 || 在遇到 true 時立即停止運算,所以 alert 沒有運行。

    有時,人們利用這個特性,只在左側的條件為假時才執(zhí)行命令。

&&(與)

兩個 & 符號表示 && 與運算符:

result = a && b;

在傳統(tǒng)的編程中,當兩個操作數(shù)都是真值時,與運算返回 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( 'Time is 12:30' );
}

就像或運算一樣,與運算的操作數(shù)可以是任意類型的值:

if (1 && 0) { // 作為 true && false 來執(zhí)行
  alert( "won't work, because the result is falsy" );
}

與運算尋找第一個假值

給出多個參加與運算的值:

result = value1 && value2 && value3;

與運算 && 做了如下的事:

  • 從左到右依次計算操作數(shù)。
  • 在處理每一個操作數(shù)時,都將其轉化為布爾值。如果結果是 ?false?,就停止計算,并返回這個操作數(shù)的初始值。
  • 如果所有的操作數(shù)都被計算過(例如都是真值),則返回最后一個操作數(shù)。

換句話說,與運算返回第一個假值,如果沒有假值就返回最后一個值。

上面的規(guī)則和或運算很像。區(qū)別就是與運算返回第一個假值,而或運算返回第一個真值。

例如:

// 如果第一個操作數(shù)是真值,
// 與運算返回第二個操作數(shù):
alert( 1 && 0 ); // 0
alert( 1 && 5 ); // 5

// 如果第一個操作數(shù)是假值,
// 與運算將直接返回它。第二個操作數(shù)會被忽略
alert( null && 5 ); // null
alert( 0 && "no matter what" ); // 0

我們也可以在一行代碼上串聯(lián)多個值。查看第一個假值是如何被返回的:

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

如果所有的值都是真值,最后一個值將會被返回:

alert( 1 && 2 && 3 ); // 3,最后一個值

與運算 ?>>? 在或運算 ?||? 之前進行

與運算 && 的優(yōu)先級比或運算 || 要高。

所以代碼 a && b || c && d 跟 && 表達式加了括號完全一樣:(a && b) || (c && d)

不要用 ?||? 或 ?>>? 來取代 ?if?

有時候,有人會將與運算符 && 作為“簡化 if”的一種方式。

例如:

let x = 1;

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

&& 右邊的代碼只有運算抵達到那里才能被執(zhí)行。也就是,當且僅當 (x > 0) 為真。

所以我們基本可以類似地得到:

let x = 1;

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

雖然使用 && 寫出的變體看起來更短,但 if 更明顯,并且往往更具可讀性。因此,我們建議根據(jù)每個語法結構的用途來使用:如果我們想要 if,就使用 if;如果我們想要邏輯與,就使用 &&。

!(非)

感嘆符號 ! 表示布爾非運算符。

語法相當簡單:

result = !value;

邏輯非運算符接受一個參數(shù),并按如下運作:

  1. 將操作數(shù)轉化為布爾類型:?true/false?。
  2. 返回相反的值。

例如:

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

兩個非運算 !! 有時候用來將某個值轉化為布爾類型:

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

也就是,第一個非運算將該值轉化為布爾類型并取反,第二個非運算再次取反。最后我們就得到了一個任意值到布爾值的轉化。

有一個略顯冗長的方式也可以實現(xiàn)同樣的效果 —— 一個內(nèi)建的 Boolean 函數(shù):

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

非運算符 ! 的優(yōu)先級在所有邏輯運算符里面最高,所以它總是在 && 和 || 之前執(zhí)行。

任務


或運算的結果是什么?

重要程度: 5

如下代碼將會輸出什么?

alert( null || 2 || undefined );

解決方案

結果是 2,這是第一個真值。

alert( null || 2 || undefined );

或運算和 alert 的結果是什么?

重要程度: 3

下面的代碼將會輸出什么?

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

解決方案

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

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

對 alert 的調(diào)用沒有返回值?;蛘哒f返回的是 undefined。

  1. 第一個或運算 ?||? 對它的左值 ?alert(1)? 進行了計算。這就顯示了第一條信息 ?1?。
  2. 函數(shù) ?alert ?返回了 ?undefined?,所以或運算繼續(xù)檢查第二個操作數(shù)以尋找真值。
  3. 第二個操作數(shù) ?2? 是真值,所以執(zhí)行就中斷了。?2? 被返回,并且被外層的 alert 顯示。

這里不會顯示 ?3?,因為運算沒有抵達 ?alert(3)?。


與操作的結果是什么?

重要程度: 5

下面這段代碼將會顯示什么?

alert( 1 && null && 2 );

解決方案

答案:null,因為它是列表中第一個假值。

alert(1 && null && 2);

與運算連接的 alert 的結果是什么?

重要程度: 3

這段代碼將會顯示什么?

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

解決方案

答案:1,然后 undefined。

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

調(diào)用 alert 返回了 undefined(它只展示消息,所以沒有有意義的返回值)。

因此,&& 計算了它左邊的操作數(shù)(顯示 1),然后立即停止了,因為 undefined 是一個假值。&& 就是尋找假值然后返回它,所以運算結束。


或運算、與運算、或運算串聯(lián)的結果

重要程度: 5

結果將會是什么?

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

解決方案

答案:3。

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

與運算 && 的優(yōu)先級比 || 高,所以它第一個被執(zhí)行。

結果是 2 && 3 = 3,所以表達式變成了:

null || 3 || 4

現(xiàn)在的結果就是第一個真值:3。


檢查值是否位于范圍區(qū)間內(nèi)

重要程度: 3

寫一個 if 條件句來檢查 age 是否位于 14 到 90 的閉區(qū)間。

“閉區(qū)間”意味著,age 的值可以取 14 或 90。


解決方案

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

檢查值是否位于范圍之外

重要程度: 3

寫一個 if 條件句,檢查 age 是否不位于 14 到 90 的閉區(qū)間。

創(chuàng)建兩個表達式:第一個用非運算 !,第二個不用。


解決方案

第一個表達式:

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

第二個表達式:

if (age < 14 || age > 90)

一個關于 "if" 的問題

重要程度: 5

下面哪一個 alert 將會被執(zhí)行?

if(...) 語句內(nèi)表達式的結果是什么?

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

解決方案

答案:第一個和第三個將會被執(zhí)行。

詳解:

// 執(zhí)行。
// -1 || 0 的結果為 -1,真值
if (-1 || 0) alert( 'first' );

// 不執(zhí)行。
// -1 && 0 = 0,假值
if (-1 && 0) alert( 'second' );

// 執(zhí)行
// && 運算的優(yōu)先級比 || 高
// 所以 -1 && 1 先執(zhí)行,給出如下運算鏈:
// null || -1 && 1  ->  null || 1  ->  1
if (null || -1 && 1) alert( 'third' );

登錄校驗

重要程度: 3

實現(xiàn)使用 prompt 進行登錄校驗的代碼。

如果訪問者輸入 "Admin",那么使用 prompt 引導獲取密碼,如果輸入的用戶名為空或者按下了 ?Esc? 鍵 —— 顯示 “Canceled”,如果是其他字符串 —— 顯示 “I don't know you”。

密碼的校驗規(guī)則如下:

  • 如果輸入的是 “TheMaster”,顯示 “Welcome!”,
  • 其他字符串 —— 顯示 “Wrong password”,
  • 空字符串或取消了輸入,顯示 “Canceled.”。

流程圖:


請使用嵌套的 if 塊。注意代碼整體的可讀性。

提示:將空字符串輸入,prompt 會獲取到一個空字符串 ''。Prompt 運行過程中,按下 ?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 塊中水平方向的縮進。技術上是非必需的,但會提升代碼的可讀性。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號