本文 AI 產出,尚未審核

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,造成錯誤判斷。 加上括號明確運算順序,或分開寫成兩行。

最佳實踐

  1. 保持一致的風格:在團隊中統一使用 === / !==,以及選擇 +=a = a + b 的慣例。
  2. 善用 const:若變數不需要重新賦值,使用 const,可以避免不小心改寫。
  3. 避免隱式型別轉換:在重要的算術運算前,使用 Number()parseFloat() 等顯式轉型。
  4. 利用短路賦值簡化設定config.timeout ??= 5000; 能在一行完成「若未設定則使用預設」的需求。
  5. 使用 Lint 工具:如 ESLint 的 no-cond-assigneqeqeqno-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 請求時,常需要把使用者傳入的參數與預設值合併,??= 能避免 0false 被錯誤覆蓋:

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 程式碼。祝你編程順利!