Learn AND, OR, XOR, NOT, and bit shift operations in JavaScript with real-world use cases like flags, permissions, and data encoding.
Bitwise operators work on the binary representation of numbers, operating on individual bits rather than the whole number. JavaScript converts numbers to 32-bit signed integers before performing bitwise operations.
// In binary:
// 5 = 00000000 00000000 00000000 00000101
// 3 = 00000000 00000000 00000000 00000011
// 5 & 3 = 00000000 00000000 00000000 00000001 = 1
console.log(5 & 3); // 1
&)Returns 1 only when both bits are 1:
5 & 3 // 001 → 1
12 & 10 // 1000 AND 1010 → 1000 = 8
255 & 0xFF // 255 (common mask pattern)
|)Returns 1 when at least one bit is 1:
5 | 3 // 111 → 7
8 | 4 // 1000 OR 0100 → 1100 = 12
^)Returns 1 when exactly one bit is 1 (bits differ):
5 ^ 3 // 110 → 6
a ^ a // Always 0 (any number XOR itself = 0)
a ^ 0 // Always a (any number XOR 0 = itself)
~)Flips every bit. In 32-bit signed integers, this is equivalent to -(n + 1):
~5 // -6
~0 // -1
~-1 // 0
<<)Shifts bits left, filling with zeros. Equivalent to multiplying by 2^n:
1 << 0 // 1
1 << 1 // 2
1 << 2 // 4
1 << 10 // 1024
3 << 2 // 12 (3 * 4)
>> and >>>)>> Signed right shift — preserves the sign bit>>> Unsigned right shift — always fills with zeros8 >> 1 // 4 (8 / 2)
-8 >> 1 // -4 (preserves negative)
-8 >>> 1 // 2147483644 (treats as unsigned 32-bit)
const PERMISSIONS = {
READ: 1 << 0, // 0001 = 1
WRITE: 1 << 1, // 0010 = 2
EXECUTE: 1 << 2, // 0100 = 4
ADMIN: 1 << 3, // 1000 = 8
};
// Grant permissions
let userPerms = PERMISSIONS.READ | PERMISSIONS.WRITE; // 3
// Check permission
const canRead = (userPerms & PERMISSIONS.READ) !== 0; // true
const canAdmin = (userPerms & PERMISSIONS.ADMIN) !== 0; // false
// Grant admin
userPerms |= PERMISSIONS.ADMIN; // 11
// Revoke write
userPerms &= ~PERMISSIONS.WRITE; // 9
// Round down to integer (faster than Math.floor for positive numbers)
const floor = n => n | 0;
floor(4.7); // 4
floor(-4.7); // -4 (careful — different from Math.floor!)
// Check if number is odd
const isOdd = n => (n & 1) === 1;
isOdd(7); // true
isOdd(8); // false
// Power of 2 check
const isPowerOf2 = n => n > 0 && (n & (n - 1)) === 0;
isPowerOf2(8); // true
isPowerOf2(12); // false
// Swap values without temp variable
let a = 5, b = 10;
a ^= b; b ^= a; a ^= b;
// a = 10, b = 5
// Store two 8-bit values in one 16-bit integer
function pack(high, low) {
return ((high & 0xFF) << 8) | (low & 0xFF);
}
function unpack(packed) {
return { high: (packed >> 8) & 0xFF, low: packed & 0xFF };
}
const packed = pack(200, 50); // 51250
unpack(packed); // { high: 200, low: 50 }
// Find the only non-duplicate in an array
function findUnique(arr) {
return arr.reduce((acc, n) => acc ^ n, 0);
}
findUnique([1, 2, 3, 2, 1]); // 3
32-bit limit: JavaScript bitwise operations work on 32-bit integers. Numbers larger than 2^31-1 are truncated:
2147483647 | 0 // 2147483647 (max 32-bit signed)
2147483648 | 0 // -2147483648 (wraps around!)
BigInt alternative: For 64-bit bitwise operations, use BigInt:
const big = 9007199254740993n;
const mask = 0xFFFFFFFFFFFFFFFFn;
big & mask; // Works correctly
Bitwise logic is much easier to understand visually. HeoLab's Bitwise Calculator shows operations in binary, octal, decimal, and hex simultaneously, with a 32-bit visual breakdown.
Bitwise operations are fast, compact, and elegant for the right problems. Use them for permission systems, feature flags, compact data encoding, and low-level algorithms. Just remember the 32-bit limitation and you'll have a powerful tool in your JavaScript toolkit.