JavaScript 課程 — 運算子(Operators)
主題:賦值運算子(=、+=、-=、…)
簡介
在程式語言中,賦值運算子是最常使用、也是最基礎的工具之一。它負責把右側的值「寫入」左側的變數,讓程式能夠儲存、更新與傳遞資料。
在 JavaScript 裡,除了最簡單的 =(直接賦值)之外,還提供了一系列複合賦值運算子(如 +=、-=、*=、/=、%=、**= 等),可以在一次運算中同時完成「取值」與「更新」的工作。熟練這些運算子不只能讓程式碼更簡潔,也能提升執行效能與可讀性,是每位前端開發者必備的功力。
本篇文章將從概念說明、實作範例、常見陷阱與最佳實踐,逐步帶你掌握賦值運算子的使用方式,並提供實務上的應用情境,讓你在寫 JavaScript 時更加得心應手。
核心概念
1. 基本賦值 (=)
= 是最直觀的賦值運算子,將右邊的表達式結果寫入左邊的變數。
let count = 0; // 把數值 0 賦值給變數 count
const name = "Alice"; // 常數 name 只能被賦值一次
注意:
=與比較運算子==、===完全不同,前者是「寫入」,後者是「比較」。
2. 複合賦值運算子
複合賦值運算子結合了算術/位元運算與賦值兩個步驟,語法為 左值 <運算子>= 右值。常見的有:
| 運算子 | 等價寫法 | 說明 |
|---|---|---|
+= |
a = a + b |
加法與賦值 |
-= |
a = a - b |
減法與賦值 |
*= |
a = a * b |
乘法與賦值 |
/= |
a = a / b |
除法與賦值 |
%= |
a = a % b |
餘數與賦值 |
**= |
a = a ** b |
次方與賦值 |
<<= |
a = a << b |
左位移與賦值 |
>>= |
a = a >> b |
右位移與賦值 |
&= |
a = a & b |
位元 AND 與賦值 |
| ` | =` | a = a | b |
^= |
a = a ^ b |
位元 XOR 與賦值 |
&&= |
a &&= b |
短路 AND 與賦值 |
| ` | =` | |
??= |
a ??= b |
空值合併與賦值 |
小技巧:使用複合賦值時,左側的變數只會被求值 一次,這對於有副作用的表達式(例如函式呼叫)非常重要。
3. 賦值與型別轉換
在 JavaScript 中,= 不會自動做型別轉換,右側的值會以原本的型別寫入左側變數。但在使用 += 時,若左側是字串,會觸發字串相加的行為:
let msg = "Score: ";
msg += 100; // 等同於 msg = msg + 100,結果是 "Score: 100"
若左側是數值,則會執行算術加法:
let total = 5;
total += 3; // total = 8
4. 解構賦值 (Destructuring Assignment)
ES6 引入的解構賦值允許一次取出陣列或物件的多個屬性,寫法上仍屬於賦值運算子的一種擴充:
// 陣列解構
const [x, y] = [10, 20]; // x = 10, y = 20
// 物件解構
const { name, age } = { name: "Bob", age: 30 };
雖然語法較長,但在處理大量資料時能大幅提升可讀性。
程式碼範例
以下範例示範常見的賦值運算子用法,包含註解說明每一步的意圖。
範例 1:累加與累減
let score = 0;
// 玩家每答對一題,加 10 分
score += 10; // 等同於 score = score + 10
console.log(score); // 10
// 若答錯,扣 5 分
score -= 5; // 等同於 score = score - 5
console.log(score); // 5
範例 2:計算總價與稅金
let price = 1200; // 商品原價
const taxRate = 0.08; // 8% 稅率
// 加上稅金(一次完成)
price += price * taxRate; // price = price + price * taxRate
console.log(`含稅金額:${price}`); // 1296
範例 3:字串拼接與模板字面量
let greeting = "Hello";
greeting += ", World!"; // 字串相加
console.log(greeting); // Hello, World!
// 使用模板字面量寫法更直觀
const name = "Jane";
const message = `Hi ${name}, welcome!`;
console.log(message);
範例 4:位元運算與旗標 (Flag) 控制
let permission = 0b0000; // 四位二進位,全部關閉
// 開啟第 2 位 (READ) 與第 4 位 (EXECUTE)
permission |= 0b0010; // READ
permission |= 0b1000; // EXECUTE
console.log(permission.toString(2)); // 1010
// 檢查 EXECUTE 是否開啟
const hasExecute = (permission & 0b1000) !== 0;
console.log(`EXECUTE: ${hasExecute}`); // true
範例 5:短路賦值 (Logical Assignment)
let config = { timeout: 0, retries: undefined };
// 若 timeout 為 falsy,設定預設值 3000
config.timeout ||= 3000; // 等同於 config.timeout = config.timeout || 3000
console.log(config.timeout); // 3000
// 若 retries 為 undefined,設定預設值 3
config.retries ??= 3; // 等同於 config.retries = config.retries ?? 3
console.log(config.retries); // 3
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方式 |
|---|---|---|
誤用 = 取代比較 |
在 if (a = b) 中,= 會把 b 賦值給 a,結果永遠為真。 |
使用 === 或 ==,並在 IDE 開啟「賦值在條件式」警告。 |
+= 產生隱式型別轉換 |
若左側是字串,+= 會執行字串拼接,可能導致意外的結果。 |
明確使用 Number() 或 parseInt() 轉型,或先檢查型別。 |
| 複合賦值左側有副作用 | obj.prop++ 與 obj.prop += 1 的差異在於 obj.prop 只會求值一次。若左側是函式呼叫,會重複執行。 |
避免在左側寫入會產生副作用的表達式,改為先存到暫存變數。 |
短路賦值與 null / undefined 的混淆 |
` | |
| 位元運算的優先順序 | a &= b == c 可能被解讀為 (a &= b) == c,造成錯誤判斷。 |
加上括號明確運算順序,或分開寫成兩行。 |
最佳實踐
- 保持一致的風格:在團隊中統一使用
===/!==,以及選擇+=或a = a + b的慣例。 - 善用
const:若變數不需要重新賦值,使用const,可以避免不小心改寫。 - 避免隱式型別轉換:在重要的算術運算前,使用
Number()、parseFloat()等顯式轉型。 - 利用短路賦值簡化設定:
config.timeout ??= 5000;能在一行完成「若未設定則使用預設」的需求。 - 使用 Lint 工具:如 ESLint 的
no-cond-assign、eqeqeq、no-implicit-coercion規則,可自動捕捉上述常見錯誤。
實際應用場景
1. 動態表單計算
在電商網站的購物車中,常需要根據使用者選擇的商品數量即時計算總金額。使用 += 可以在每次點擊「加入」時快速累加:
let total = 0;
function addItem(price, qty = 1) {
total += price * qty; // 累加本次加入的金額
document.getElementById('total').textContent = `$${total.toFixed(2)}`;
}
2. 旗標管理 (Feature Toggle)
大型前端應用常用位元旗標來控制功能開關,|=、&=、^= 能讓開關切換變得一目了然:
const FEATURE = {
DARK_MODE: 1 << 0, // 0001
BETA_UI: 1 << 1, // 0010
LOGGING: 1 << 2 // 0100
};
let enabled = 0;
// 開啟暗色模式與日誌功能
enabled |= FEATURE.DARK_MODE | FEATURE.LOGGING;
// 關閉 Beta UI(若有開啟的話)
enabled &= ~FEATURE.BETA_UI;
3. 設定預設值與合併選項
在建立 API 請求時,常需要把使用者傳入的參數與預設值合併,??= 能避免 0、false 被錯誤覆蓋:
function buildOptions(userOpts) {
const opts = { timeout: 2000, cache: true };
opts.timeout ??= userOpts.timeout; // 若未提供 timeout,使用預設
opts.cache ??= userOpts.cache; // 同上
return opts;
}
總結
賦值運算子是 JavaScript 中不可或缺的基礎工具,從最簡單的 = 到功能強大的複合賦值(+=、-=、||=、??= 等),它們讓我們可以在 單行程式碼 內完成「取值 + 更新」的工作,提升程式的可讀性與效能。
掌握以下要點,即可在日常開發中游刃有餘:
- 正確區分賦值與比較,避免因
=誤用而產生邏輯錯誤。 - 注意型別轉換,特別是
+=在字串與數值混用時的行為。 - 利用短路賦值 (
||=,&&=,??=) 簡化設定預設值的流程。 - 遵循最佳實踐:使用
const、Lint 規則、顯式轉型,減少隱藏的 bug。
透過本文的概念說明、實作範例與實務案例,希望你能在開發過程中更加自信地使用賦值運算子,寫出 簡潔、可靠且易維護 的 JavaScript 程式碼。祝你編程順利!