本文 AI 產出,尚未審核

JavaScript 位元運算子(Operators)深入探討


簡介

在日常的前端開發中,我們大多使用算術運算子(+、-、*、/)或比較運算子(===、!==)來處理資料。但在處理底層資料、效能敏感的演算法或是需要直接操控二進位資料時,位元運算子(Bitwise Operators)則是不可或缺的工具。

位元運算子直接對 32 位元的二進位表示進行操作,執行速度極快,且常用於:

  • 旗標(Flag)管理:使用位元來同時保存多個布林狀態。
  • 加密/解密:簡易的 XOR 加密、位元旋轉等。
  • 圖形與音訊處理:像素合成、色彩通道分離等。

本篇文章將從概念、語法、實作範例一路帶你了解 JavaScript 的位元運算子,並提供常見陷阱與最佳實踐,幫助你在實務中安全、有效地運用這些工具。


核心概念

1. 位元運算子概覽

運算子 說明 範例 (10)
& 位元 AND,兩位皆為 1 時結果為 1 10 & 6 // 0010
| 位元 OR,兩位至少有一個 1 時結果為 1 10 | 6 // 1110
^ 位元 XOR,兩位不同時結果為 1 10 ^ 6 // 1100
~ 位元 NOT,對每一位取反 ~10 // -11
<< 左移,將二進位左移 n 位,右側補 0 5 << 2 // 20
>> 右移(保留符號位),左側補原符號位 -8 >> 2 // -2
>>> 零填充右移,左側永遠補 0(不保留符號) -8 >>> 2 // 1073741822

注意:在 JavaScript 中,所有位元運算都會先把操作數 轉成 32 位有號整數ToInt32),因此浮點數會被截斷。

2. 兩補數(Two's Complement)

負數在位元層面的表示方式是 兩補數。簡單說,負數的二進位等於其絕對值的二進位取反(~),再加 1。了解兩補數可幫助你解讀 ~>>>>> 的行為。

// 10 的二進位 (32 位) : 00000000 00000000 00000000 00001010
// ~10 的結果 : 11111111 11111111 11111111 11110101 (等於 -11)
console.log(~10); // -11

3. 位元與布林值的關係

在 JavaScript 中,0 代表 false,非 0 代表 true。因此位元運算常與布林值結合,用來檢查或設定特定位元。

const FLAG_READ   = 1 << 0; // 0001
const FLAG_WRITE  = 1 << 1; // 0010
const FLAG_EXEC   = 1 << 2; // 0100

let permission = FLAG_READ | FLAG_WRITE; // 0011

// 檢查是否有寫入權限
const canWrite = (permission & FLAG_WRITE) !== 0;
console.log(canWrite); // true

4. 常見的位元技巧

技巧 說明 範例
設定旗標 `value = FLAG`
清除旗標 value &= ~FLAG perm &= ~FLAG_WRITE;
切換旗標 value ^= FLAG perm ^= FLAG_READ;
檢查是否為偶數 num & 1 為 0 時為偶數 if ((num & 1) === 0) …
快速乘除 2 的冪 左右移位 x << 3 等於 x * 8

程式碼範例

以下提供 5 個實務上常見且易於理解的範例,說明每個位元運算子的用法與注意點。

範例 1:旗標(Flag)管理

// 定義三個權限旗標
const READ   = 1 << 0; // 0001
const WRITE  = 1 << 1; // 0010
const EXEC   = 1 << 2; // 0100

let userPerm = 0; // 初始無任何權限

// 設定 READ 與 EXEC 權限
userPerm |= READ | EXEC; // 0101

// 檢查是否具備 WRITE 權限
const hasWrite = (userPerm & WRITE) !== 0;
console.log('hasWrite?', hasWrite); // false

// 移除 EXEC 權限
userPerm &= ~EXEC; // 0001
console.log('new perm:', userPerm.toString(2).padStart(4, '0')); // 0001

範例 2:快速判斷奇偶

function isEven(num) {
  // 只要最低位是 0 就是偶數
  return (num & 1) === 0;
}
function isOdd(num) {
  return (num & 1) === 1;
}
console.log(isEven(42)); // true
console.log(isOdd(42));  // false

範例 3:XOR 加密(簡易)

// 使用單一位元鍵值做 XOR 加密/解密
function xorCipher(text, key) {
  const result = [];
  for (let i = 0; i < text.length; i++) {
    // 文字的 Unicode 轉成 16 位元,與 key XOR 後再轉回字元
    result.push(String.fromCharCode(text.charCodeAt(i) ^ key));
  }
  return result.join('');
}

const secret = xorCipher('Hello World!', 0x5A);
console.log('cipher:', secret);        // 加密後的亂碼
console.log('plain :', xorCipher(secret, 0x5A)); // 再次 XOR 復原

範例 4:32 位元的顏色通道分離

// 32 位元 ARGB 顏色值: 0xAARRGGBB
function splitColor(argb) {
  const a = (argb >>> 24) & 0xFF;
  const r = (argb >>> 16) & 0xFF;
  const g = (argb >>> 8)  & 0xFF;
  const b = argb & 0xFF;
  return { a, r, g, b };
}
const color = 0xFF3366CC; // 不透明、R=0x33、G=0x66、B=0xCC
console.log(splitColor(color)); // { a: 255, r: 51, g: 102, b: 204 }

範例 5:零填充右移處理大於 2^31 的正整數

// JavaScript 內部使用有號 32 位元,直接右移會保留符號位
let large = 0xF0000000; // 4026531840 (超過 2^31)
console.log(large >> 4);   // -268435456 (保留符號)
console.log(large >>> 4);  // 268435456  (正確的零填充右移)

常見陷阱與最佳實踐

陷阱 描述 建議的解決方式
自動轉型為 32 位有號整數 浮點數、BigInt、字串會被隱式轉成 32 位整數,可能導致意外的截斷。 在使用前顯式轉型>>> 0)或檢查類型。
負數右移的符號位延伸 (>>) 右移負數會保留負號,結果不符合「除以 2 的次方」的直覺。 若需要純粹的除以 2 的次方,使用 >>> 再自行處理符號。
位元運算不支援 64 位以上 JavaScript 位元運算只能處理 32 位,對大於 2^31-1 的正整數會失真。 需要更大位元時,使用 BigInt 搭配自訂演算法或第三方函式庫。
優先順序混淆 位元運算子優先級低於算術運算子,易產生錯誤的運算結果。 加上括號明確運算順序,或參考 MDN 的運算子優先表。
使用 ~ 取反時的負數 ~num 會得到 -(num+1),常被誤以為是「正數取反」的結果。 若僅想要 位元翻轉(例如 8 位元),先 & 0xFF 再使用 ~,最後再 & 0xFF

最佳實踐

  1. 保持可讀性:使用具意義的常數(如 FLAG_READ)取代直接寫數字。
  2. 用括號明確運算(value & MASK) !== 0value & MASK !== 0 更不易出錯。
  3. 避免在高階邏輯中混用位元與布林:雖然 0/1 可直接當布林,但寫清楚 !== 0 能提升可讀性。
  4. 測試邊界值:特別是負數、超過 2^31 的正整數,確保行為符合預期。
  5. 考慮未來的可維護性:若旗標集合會擴增,建議使用 EnumObject.freeze 方式管理。

實際應用場景

場景 為什麼使用位元運算子 範例簡述
權限系統 只需 1 個整數即可儲存多個布林權限,省記憶體且查詢 O(1)。 文章管理平台的 READ、WRITE、DELETE 權限。
資料壓縮 把多個 0/1 狀態打包成單一位元,減少網路傳輸量。 WebSocket 訊息的 flag 位元欄位。
圖形渲染 直接操作像素的 RGBA 通道,速度比使用陣列操作快。 Canvas 中的 ImageData 逐像素處理。
加密/雜湊 XOR、循環位移(rotate)是許多簡易加密演算法的基礎。 簡易的「一次性密碼」生成器。
演算法優化 用位元技巧取代除法或乘法(如 x << 3 代替 x * 8)提升效能。 大型資料集合的排序或分組。

總結

位元運算子是 JavaScript 中高效且功能強大的工具,雖然在日常 UI 開發裡不常見,但在需要底層控制、效能優化或多旗標管理的情境下,掌握它們會讓你的程式碼更簡潔、執行更快速。

本文從概念、語法、實作範例、常見陷阱與最佳實踐,最後列出實務應用場景,提供了完整的學習路徑。建議在實際專案中先從旗標管理奇偶判斷等簡單案例練習,逐步擴展到顏色通道分離、XOR 加密等較高階的應用。

只要保持清晰的意圖、嚴謹的測試,位元運算子將成為你 JavaScript 工具箱裡不可或缺的一員。祝你在程式世界裡「位」元不移,寫出更佳的程式碼!