Javascript 邏輯運(yùn)算符

2023-02-17 10:37 更新

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

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

讓我們來(lái)詳細(xì)看一下。

||(或)

兩個(gè)豎線符號(hào)表示“或”運(yùn)算符:

result = a || b;

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

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

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

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

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

如果操作數(shù)不是布爾值,那么它將會(huì)被轉(zhuǎn)化為布爾值來(lái)參與運(yùn)算。

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

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

大多數(shù)情況下,邏輯或 || 會(huì)被用在 if 語(yǔ)句中,用來(lái)測(cè)試是否有 任何 給定的條件為 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.' ); // 是周末
}

或運(yùn)算尋找第一個(gè)真值

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

拓展的算法如下所示。

給定多個(gè)參與或運(yùn)算的值:

result = value1 || value2 || value3;

或運(yùn)算符 || 做了如下的事情:

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

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

換句話說(shuō),一個(gè)或運(yùn)算 ?||? 的鏈,將返回第一個(gè)真值,如果不存在真值,就返回該鏈的最后一個(gè)值。

例如:

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

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

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

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

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

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

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

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

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

    這指的是,|| 對(duì)其參數(shù)進(jìn)行處理,直到達(dá)到第一個(gè)真值,然后立即返回該值,而無(wú)需處理其他參數(shù)。

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

    在下面這個(gè)例子中,只會(huì)打印第二條信息:

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

    在第一行中,或運(yùn)算符 || 在遇到 true 時(shí)立即停止運(yùn)算,所以 alert 沒(méi)有運(yùn)行。

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

&&(與)

兩個(gè) & 符號(hào)表示 && 與運(yùn)算符:

result = a && b;

在傳統(tǒng)的編程中,當(dāng)兩個(gè)操作數(shù)都是真值時(shí),與運(yùn)算返回 true,否則返回 false

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

帶有 if 語(yǔ)句的示例:

let hour = 12;
let minute = 30;

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

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

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

與運(yùn)算尋找第一個(gè)假值

給出多個(gè)參加與運(yùn)算的值:

result = value1 && value2 && value3;

與運(yùn)算 && 做了如下的事:

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

換句話說(shuō),與運(yùn)算返回第一個(gè)假值,如果沒(méi)有假值就返回最后一個(gè)值。

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

例如:

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

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

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

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

如果所有的值都是真值,最后一個(gè)值將會(huì)被返回:

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

與運(yùn)算 ?>>? 在或運(yùn)算 ?||? 之前進(jìn)行

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

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

不要用 ?||? 或 ?>>? 來(lái)取代 ?if?

有時(shí)候,有人會(huì)將與運(yùn)算符 && 作為“簡(jiǎn)化 if”的一種方式。

例如:

let x = 1;

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

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

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

let x = 1;

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

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

!(非)

感嘆符號(hào) ! 表示布爾非運(yùn)算符。

語(yǔ)法相當(dāng)簡(jiǎn)單:

result = !value;

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

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

例如:

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

兩個(gè)非運(yùn)算 !! 有時(shí)候用來(lái)將某個(gè)值轉(zhuǎn)化為布爾類型:

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

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

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

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

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

任務(wù)


或運(yùn)算的結(jié)果是什么?

重要程度: 5

如下代碼將會(huì)輸出什么?

alert( null || 2 || undefined );

解決方案

結(jié)果是 2,這是第一個(gè)真值。

alert( null || 2 || undefined );

或運(yùn)算和 alert 的結(jié)果是什么?

重要程度: 3

下面的代碼將會(huì)輸出什么?

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

解決方案

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

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

對(duì) alert 的調(diào)用沒(méi)有返回值?;蛘哒f(shuō)返回的是 undefined。

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

這里不會(huì)顯示 ?3?,因?yàn)檫\(yùn)算沒(méi)有抵達(dá) ?alert(3)?。


與操作的結(jié)果是什么?

重要程度: 5

下面這段代碼將會(huì)顯示什么?

alert( 1 && null && 2 );

解決方案

答案:null,因?yàn)樗橇斜碇械谝粋€(gè)假值。

alert(1 && null && 2);

與運(yùn)算連接的 alert 的結(jié)果是什么?

重要程度: 3

這段代碼將會(huì)顯示什么?

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

解決方案

答案:1,然后 undefined。

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

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

因此,&& 計(jì)算了它左邊的操作數(shù)(顯示 1),然后立即停止了,因?yàn)?nbsp;undefined 是一個(gè)假值。&& 就是尋找假值然后返回它,所以運(yùn)算結(jié)束。


或運(yùn)算、與運(yùn)算、或運(yùn)算串聯(lián)的結(jié)果

重要程度: 5

結(jié)果將會(huì)是什么?

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

解決方案

答案:3。

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

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

結(jié)果是 2 && 3 = 3,所以表達(dá)式變成了:

null || 3 || 4

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


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

重要程度: 3

寫(xiě)一個(gè) if 條件句來(lái)檢查 age 是否位于 14 到 90 的閉區(qū)間。

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


解決方案

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

檢查值是否位于范圍之外

重要程度: 3

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

創(chuàng)建兩個(gè)表達(dá)式:第一個(gè)用非運(yùn)算 !,第二個(gè)不用。


解決方案

第一個(gè)表達(dá)式:

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

第二個(gè)表達(dá)式:

if (age < 14 || age > 90)

一個(gè)關(guān)于 "if" 的問(wèn)題

重要程度: 5

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

if(...) 語(yǔ)句內(nèi)表達(dá)式的結(jié)果是什么?

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

解決方案

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

詳解:

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

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

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

登錄校驗(yàn)

重要程度: 3

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

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

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

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

流程圖:


請(qǐng)使用嵌套的 if 塊。注意代碼整體的可讀性。

提示:將空字符串輸入,prompt 會(huì)獲取到一個(gè)空字符串 ''。Prompt 運(yùn)行過(guò)程中,按下 ?ESC? 鍵會(huì)得到 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" );
}

請(qǐng)注意 if 塊中水平方向的縮進(jìn)。技術(shù)上是非必需的,但會(huì)提升代碼的可讀性。


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

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)