前端問答:JavaScript 中的 "??" 和 "||" 有啥不同
在 JavaScript 開發(fā)中,很多小伙伴都會遇到一個場景,就是要給變量設(shè)置一個默認(rèn)值,比如當(dāng)變量沒有有效值時,使用一個備用值。這個時候,可能有兩個操作符會讓你感到困惑:??(空值合并運算符)和 ||(邏輯或運算符)。一開始看,它們似乎都能達到相同的效果,但其實它們背后的邏輯完全不同,適用的場景也不一樣。今天我們就來聊聊這兩者的區(qū)別,幫你快速上手,避免掉坑!
一、"||" 是怎么工作的?—— 就像找備胎一樣!
|| 就好像是在說:“如果這個不行,就換另一個?!彼木唧w作用是檢查左邊的值(第一個計劃),如果左邊這個值是 “假”(也就是不靠譜、不能用的意思),它就會去看右邊的值(第二個計劃),并返回右邊的值。
舉個例子來理解一下:
let result = value1 || value2;
這段代碼就是在說:“如果 value1 能用,就用 value1;如果 value1 不行,那就用 value2 吧!”那問題來了,什么情況下 value1 會不行呢?
1. 什么是 "假值"?
在 JavaScript 里,有一些特殊的值會被認(rèn)為是“假”的,像這些:
- false(假)
- 0(零)
- ""(空字符串)
- null(表示空)
- undefined(未定義)
- NaN(非數(shù)字)
這些值都被認(rèn)為是不能用的“假值”。如果你遇到這些值,|| 操作符就會幫你返回后面的備用計劃。
2. 舉個栗子??:
let username = "" || "游客"; // 返回 "游客"
let score = 0 || 10; // 返回 10
在上面的例子里,""(空字符串)和 0 都是“假值”,所以 JavaScript 會忽略它們,直接選擇后面的“游客”和 10 作為返回值。
3. 那什么時候用 "||" 呢?
|| 的最佳使用場景就是當(dāng)你不確定一個值是否靠譜的時候,你可以為它準(zhǔn)備一個備用值。就像生活中你遇到的兩手準(zhǔn)備:如果第一種方案失敗了,立刻執(zhí)行第二種。
比如你想讓用戶輸入他們的名字,但如果用戶沒有輸入名字,你就讓系統(tǒng)自動叫他們“游客”。用 || 來實現(xiàn)這個需求簡直再好不過了:
function greetUser(name) {
let username = name || "游客";
console.log(`你好, ${username}!`);
}
greetUser(""); // 輸出: 你好, 游客!
greetUser("Alice"); // 輸出: 你好, Alice!
在這個例子里,如果用戶沒有輸入名字(比如空字符串 ""),我們就用 || 返回一個默認(rèn)值“游客”。但如果用戶輸入了名字(比如 “Alice”),我們就用這個名字打招呼。
二、JavaScript 中的"??"操作符:只關(guān)心空值,別搞混了!
JavaScript 里的??(空值合并運算符)看起來和我們之前聊過的||有點像,但它其實更“挑剔”!如果說||是遇到假值就找備胎,那么??只會在 null 和 undefined 出現(xiàn)時才出手救場。今天咱們就來深入了解一下這個 ECMAScript 2020 (ES11) 中的新工具。
1. "??" 是怎么工作的?—— 只處理“真正”的空值
?? 操作符的工作方式是:當(dāng)左邊的值是 null 或 undefined 時,才返回右邊的備用值。像 0、false、"" 這些“假值”,在??眼里都是 有效值,它們不會觸發(fā)備用計劃。
語法很簡單:
let result = value1 ?? value2;
意思是:如果 value1 是 null 或 undefined,那就返回 value2;否則就直接用 value1。
2. 舉個例子??:
let username = "" ?? "游客"; // 返回 ""
let score = 0 ?? 10; // 返回 0
在這兩個例子中,空字符串 "" 和 0 雖然是“假值”,但因為它們不是 null 或 undefined,?? 不會認(rèn)為它們無效,所以直接返回它們本身。這樣就避免了 || 操作符那種“誤判”的情況——如果你真的想用 0 或 "" 作為有效值,?? 就非常合適。
3. 什么時候用"??"?
?? 非常適合在你 只想處理null或undefined 的場景下使用。比方說,你希望某個變量可以是 0 或 false 這樣的值,而不想讓它們觸發(fā)默認(rèn)值。來看個小例子:
function getScore(score) {
return score ?? 10;
}
console.log(getScore(0)); // 輸出: 0
console.log(getScore(null)); // 輸出: 10
在這個例子里,score 可以是 0,也可以是 null。如果傳入 0,?? 不會把它當(dāng)成空值,而是直接返回 0。但如果 score 是 null,它就會返回默認(rèn)值 10。這種行為對于某些場景非常有用,比如分?jǐn)?shù)為 0 的時候你不希望它被誤當(dāng)成無效值。
三、?? 和 || 的關(guān)鍵區(qū)別:用錯容易踩坑哦!
在 JavaScript 里,??(空值合并運算符)和 ||(邏輯或運算符)都是用來設(shè)置默認(rèn)值的利器,初學(xué)者可能覺得它們差不多,但其實它們的行為有很大不同。為了避免代碼里的坑,我們必須清楚兩者的使用場景和差異。
1. "Falsy" vs. "Nullish":誰才是真的空?
||:它的邏輯是,遇到任何“假值”(falsy)就會返回備用值。這些“假值”包括:
- false
- 0
- ""(空字符串)
- null
- undefined
- NaN(非數(shù)字)
只要值是這些“假值”,|| 就會觸發(fā)備用值。
??:相比之下,?? 只關(guān)注 null 和undefined。也就是說,像 0、false 和 "" 這些雖然看似“假”的值,但在 ?? 眼中是有效的,不會觸發(fā)備用值。
2. 使用場景:該選誰?
- 使用 ||:當(dāng)你想對所有“假值”都提供一個備用方案時,比如 false、0 或空字符串。這種情況下,|| 是你最好的選擇。
- 使用 ??:如果你只想處理 null 和 undefined,而對 0、false 這些值保留,那么 ?? 更適合你。
3. 舉例對比:看代碼更直觀
let value1 = 0 || 5; // value1 是 5
let value2 = 0 ?? 5; // value2 是 0
let value3 = null || "默認(rèn)值"; // value3 是 "默認(rèn)值"
let value4 = null ?? "默認(rèn)值"; // value4 是 "默認(rèn)值"
在第一個例子中,value1 變成了 5,因為 0 被 || 認(rèn)為是“假值”。而在 ?? 的情況下,value2 保持為 0,因為 0 并不是 null 或 undefined,所以不會觸發(fā)備用值。
第二個例子里,null 在兩種操作符中都觸發(fā)了備用值,結(jié)果一樣。
4. 潛在的 Bug:小心別踩坑!
誤用 || 會導(dǎo)致一些你沒料到的問題,特別是當(dāng) 0、false 或 "" 是有效值時。來看個例子:
function getDiscountedPrice(price) {
return price || 100; // 這里會返回 100,即使 price 是 0
}
console.log(getDiscountedPrice(0)); // 輸出: 100(錯誤)
這里的邏輯會導(dǎo)致問題,因為 0 是一個有效的價格,但由于 || 把它當(dāng)作“假值”,最終返回了備用值 100。解決方案就是用 ??,這樣 0 就不會被誤判了:
function getDiscountedPrice(price) {
return price ?? 100; // 這里會正確返回 0
}
console.log(getDiscountedPrice(0)); // 輸出: 0(正確)
5. 短路求值:誰先說了算?
|| 和 ?? 都使用了 短路求值,意思是如果左邊的值能決定結(jié)果,右邊的值就不會被計算。但兩者的判斷標(biāo)準(zhǔn)不同——|| 會在遇到任意“假值”時短路,而 ?? 只會在 null 或 undefined 時短路。
6.總結(jié):怎么選?
- 如果你需要處理所有的“假值”(包括 0、false、""),就用 ||。
- 如果你只關(guān)心 null 和 undefined,而 0、false 這些值也是有效輸入,那就用 ??。
四、結(jié)合 || 和 ??:多重保險,讓代碼更健壯!
有時候,你可能會遇到更復(fù)雜的場景,既要處理 null 和 undefined,又需要對其他“假值”如 0、false、"" 做特殊處理。這種情況下,我們可以把 || 和 ?? 結(jié)合起來,打造一個更加全面的“多重保險”邏輯。
1. 為什么要同時用 || 和 ???
?? 負(fù)責(zé)處理 null 和 **undefined**,而 || 可以處理 所有“假值”(如 0、false、空字符串等)。有些情況下,你可能希望 null 和 undefined 返回默認(rèn)值,而對于其他“假值”則使用不同的邏輯處理。
來看個例子:
let result = (value ?? defaultValue) || fallbackValue;
在這段代碼中,我們做了兩步處理:
- value ?? defaultValue:首先,?? 檢查 value 是否為 null 或 undefined。如果是,那么返回 defaultValue;如果不是,則直接返回 value。
- || fallbackValue:接下來,結(jié)果再經(jīng)過 ||,如果這個結(jié)果是“假值”(例如 0、false、空字符串等),就返回 fallbackValue。
2. 舉個栗子
假設(shè)你在處理一個分?jǐn)?shù)系統(tǒng),null 或 undefined 的時候要用默認(rèn)分?jǐn)?shù),但同時如果分?jǐn)?shù)是 0 也不能直接忽略,需要特殊處理。我們可以這樣寫:
function getFinalScore(score) {
return (score ?? 50) || "分?jǐn)?shù)無效";
}
console.log(getFinalScore(null)); // 輸出: 50(null 被 `??` 處理)
console.log(getFinalScore(0)); // 輸出: "分?jǐn)?shù)無效"(0 被 `||` 處理)
console.log(getFinalScore(80)); // 輸出: 80(有效分?jǐn)?shù))
- null 的情況:score 是 null 時,?? 返回默認(rèn)值 50,|| 不會再觸發(fā)。
- 0 的情況:score 是 0 時,?? 不起作用,|| 將 0 視為“假值”,返回 "分?jǐn)?shù)無效"。
- 有效分?jǐn)?shù)的情況:score 是 80 時,直接返回 80。
3. 什么時候使用這種組合?
這種組合特別適合需要處理復(fù)雜邏輯的場景,比如:
- null 或 undefined 的時候,返回一個默認(rèn)值;
- 其他“假值”(如 0、false)也需要單獨處理,避免被誤認(rèn)為無效。
結(jié)束
在 JavaScript 開發(fā)中,?? 和 || 絕對是處理默認(rèn)值的利器,雖然它們看上去很像,但實際應(yīng)用中卻有明顯區(qū)別。|| 會把很多值當(dāng)作“假值”,包括 0、false、空字符串等;而 ?? 只關(guān)心 null 和 undefined。因此,合理選擇這兩個運算符,能讓你避免不必要的 Bug,尤其是在處理特殊值的時候。