本文 AI 產出,尚未審核
JavaScript 運算子教學:比較運算子 == vs ===
簡介
在 JavaScript 中,比較運算子是日常開發不可或缺的工具。它們負責判斷兩個值之間的關係,進而決定程式流程的走向。特別是 ==(相等) 與 ===(嚴格相等),雖然寫法相近,卻在背後執行的邏輯上大相逕庭。
對初學者而言,誤用 == 常會導致 隱藏的型別轉換(type coercion)問題,進而產生難以偵測的 bug。相反地,熟練掌握 === 的使用方式,能讓程式的行為更可預測、可維護。本文將深入探討兩者的差異、實作範例、常見陷阱與最佳實踐,幫助你在實務開發中正確選擇比較運算子。
核心概念
1. 基本語意
| 運算子 | 含義 | 會不會自動型別轉換? |
|---|---|---|
== |
相等(抽象相等) | 會,會嘗試將兩側的值轉成相同型別再比較 |
=== |
嚴格相等(嚴格相等) | 不會,只有在型別與值都相同時才回傳 true |
重點:
===同時檢查 型別 與 值,==只檢查 值,但在比較前會先進行型別轉換。
2. == 的型別強制轉換規則(簡化版)
- 數字 vs 字串:字串會被轉成數字再比較。
"5" == 5 // true,字串 "5" 轉成數字 5 - 布林 vs 其他:布林會先轉成 0(false)或 1(true),再與右側比較。
false == 0 // true true == 1 // true - null 與 undefined:只有
null == undefined為true,與其他任何值比較皆為false。null == undefined // true null == 0 // false - 物件 vs 原始值:物件會先呼叫
valueOf或toString取得原始值,再與右側比較。[1,2] == "1,2" // true,陣列轉成字串 "1,2"
3. === 的行為
- 不會 進行任何型別轉換。
- 只有當 左、右兩側的型別相同,且 值相等 時,才回傳
true。
5 === "5" // false,型別不相同
null === undefined // false
4. 為什麼建議預設使用 ===?
- 可預測性:程式碼的行為不會因為隱藏的型別轉換而改變。
- 維護成本低:未來同事或自己回顧程式時,能立即看出比較的意圖。
- 避免奇怪的 bug:例如
[] == false為true,但[] === false為false,前者往往不是開發者的預期。
程式碼範例
以下示範 5 個常見情境,說明 == 與 === 的差異與正確的寫法。每段程式碼皆附上中文註解。
範例 1:字串與數字的比較
// 使用 == 時,字串會被轉成數字
console.log("10" == 10); // true
// 使用 === 時,型別不同直接回傳 false
console.log("10" === 10); // false
// 正確做法:若想比較值相等,先手動轉型
console.log(Number("10") === 10); // true
範例 2:null 與 undefined 的相等性
// 只在 == 時相等,=== 永遠不相等
console.log(null == undefined); // true
console.log(null === undefined); // false
// 在判斷「是否為空」時,建議使用嚴格相等
function isEmpty(value) {
return value === null || value === undefined;
}
console.log(isEmpty(null)); // true
console.log(isEmpty(undefined)); // true
範例 3:布林值與其他類型的隱形轉換
// == 會把布林值轉成 0 / 1 再比較
console.log(false == 0); // true
console.log(true == "1"); // true
// === 不會自動轉型
console.log(false === 0); // false
console.log(true === "1"); // false
// 建議:先將左側值轉成布林,再比較
console.log(Boolean(0) === false); // true
範例 4:陣列與字串的比較
// 陣列會被自動呼叫 toString(),變成逗號分隔的字串
console.log([1, 2, 3] == "1,2,3"); // true
console.log([1, 2, 3] === "1,2,3"); // false
// 若要比較內容相等,應自行比較每個元素
function arraysEqual(a, b) {
return a.length === b.length && a.every((v, i) => v === b[i]);
}
console.log(arraysEqual([1,2,3], [1,2,3])); // true
範例 5:物件與原始值的比較
const obj = {
valueOf() { return 42; } // 當做原始值使用時會回傳 42
};
console.log(obj == 42); // true,會呼叫 valueOf()
console.log(obj === 42); // false,型別不同
// 正確的做法:顯式取得原始值再比較
console.log(obj.valueOf() === 42); // true
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方式 |
|---|---|---|
| 隱式型別轉換 | == 會自動把字串、布林、物件等轉型,導致意外相等。 |
預設使用 ===,必要時手動轉型(Number(), String(), Boolean())。 |
null 與 undefined 混用 |
null == undefined 為 true,但在大多數情況下兩者的語意不同。 |
明確檢查 value === null 或 value === undefined,或使用 value == null 只在「任意空值」的情境下。 |
| 空陣列/空字串與布林的比較 | [] == false、"" == false 都會回傳 true,容易誤判。 |
使用 Array.isArray()、typeof 或 === 進行嚴格比較。 |
NaN 的比較 |
NaN == NaN 與 NaN === NaN 都是 false。 |
使用 Number.isNaN(value) 判斷。 |
| 浮點數精度 | 0.1 + 0.2 === 0.3 為 false,因為浮點數誤差。 |
使用容差比較(Math.abs(a - b) < EPSILON)。 |
最佳實踐清單
- 統一使用
===,除非確定需要==的寬鬆比較。 - 在條件判斷前先正規化型別(例如
String(id)、Number(age))。 - 編寫 lint 規則:如 ESLint 的
eqeqeq規則,強制使用嚴格相等。 - 避免在
if內直接比較物件,改用自訂的比較函式。 - 對於可能是
null或undefined的值,使用 可選鏈(?.)或 空值合併(??)避免意外錯誤。
實際應用場景
1. 表單驗證
在前端表單驗證時,使用者輸入的值大多是 字串。若要檢查是否為空或符合特定數值,必須先轉型:
function isAdult(ageInput) {
const age = Number(ageInput); // 明確轉成數字
return age >= 18; // 直接比較,使用嚴格相等不涉及
}
2. API 回傳值檢查
後端 API 可能會回傳 null、undefined 或空字串。使用 === 可以明確區分:
fetch('/api/user')
.then(res => res.json())
.then(data => {
if (data.username === null) {
console.warn('使用者名稱未設定');
} else if (data.username === undefined) {
console.error('API 回傳結構異常');
} else {
console.log(`Hello, ${data.username}`);
}
});
3. 資料結構的 key 判斷
在物件或 Map 中查找鍵時,必須使用嚴格相等,否則可能得到錯誤結果:
const map = new Map();
map.set(1, 'one');
map.set('1', 'string one');
console.log(map.get(1)); // 'one'
console.log(map.get('1')); // 'string one'
如果不小心使用 ==,會把 1 與 '1' 混為一談,造成資料錯亂。
4. 測試斷言(unit test)
測試框架(如 Jest)建議使用 toBe(嚴格相等)而非 toEqual(深度相等)來檢查原始值:
test('strict equality', () => {
expect(5).toBe(5); // 嚴格相等
expect('5').not.toBe(5); // 不相等
});
總結
==會在比較前執行型別強制轉換,容易產生「看似相等」卻實際不符合預期的情況。===不做任何型別轉換,只有在 型別 與 值 完全相同時才回傳true,因此是 預設的安全選擇。- 在日常開發中,養成使用
===的習慣,配合手動型別轉換或工具(如 ESLint)可大幅降低 bug 風險。 - 了解兩者的底層機制,才能在特殊需求(例如寬鬆的
null/undefined判斷)時,正確且安全地使用==。
掌握比較運算子的細節,不只是寫出「能跑」的程式,更是打造 可讀、可維護、可靠 程式碼的關鍵。祝你在 JavaScript 的旅程中,能以嚴謹的比較邏輯,寫出更健壯的應用程式!