JavaScript Bitwise & Shift Operators: &, |, ^, ~, <<, >>, >>>
JavaScript bitwise and shift operators let you work with numbers at the binary level. They are useful for compact flags, masking, and fast integer-style operations, but they behave differently from normal arithmetic and comparison operators.
Quick answer: Bitwise operators treat numbers as 32-bit signed integers, so &, |, ^, and ~ operate on bits, while <<, >>, and >>> shift bits left or right.
Difficulty: Beginner
You'll understand this better if you know: basic JavaScript numbers, binary digits, and how variables store values.
1. What Are JavaScript Bitwise and Shift Operators?
Bitwise operators work on the individual bits inside a number instead of on the decimal value you normally see in code. JavaScript converts the number to a 32-bit signed integer, performs the operation, and then converts the result back to a regular number.
- & performs bitwise AND.
- | performs bitwise OR.
- ^ performs bitwise XOR.
- ~ performs bitwise NOT.
- << shifts bits left.
- >> shifts bits right while keeping the sign.
- >>> shifts bits right and fills with zeros.
These operators are different from logical operators like && and ||, which work with truthy and falsy values instead of bits.
2. Why Bitwise Operators Matter
Bitwise operators matter when you need to store many true/false settings efficiently, extract part of a number, or manipulate low-level numeric data. They also appear in browser and systems code, even if many application developers use them less often than other operators.
They are especially helpful when you want to:
- combine multiple flags into one number,
- check whether a specific flag is enabled,
- mask off unwanted bits,
- round numbers down with an integer-style operation,
- work with colors, permissions, or protocol data.
They are less useful for everyday business logic, because normal JavaScript arrays, objects, and booleans are usually easier to read.
3. Basic Syntax or Core Idea
All bitwise operators use ordinary numeric operands, but the operation happens on their binary representation. The smallest practical form looks like this:
Minimal bitwise example
The following example combines two numbers with bitwise AND. The result keeps only the bits that are set in both values.
const a = 5; // 0101 in binary
const b = 3; // 0011 in binary
const result = a & b; // 0001 in binary
console.log(result); // 1Here, 5 & 3 becomes 1 because only the last bit is set in both numbers.
Shifting bits
A left shift moves bits toward the larger place values, which usually multiplies by powers of two.
const value = 6; // 0110
const shifted = value << 1; // 1100
console.log(shifted); // 12This works because every bit moves one position to the left.
4. Step-by-Step Examples
Example 1: Bitwise OR combines flags
Bitwise OR sets a bit if either side has that bit set. This is common for combining option flags into one value.
const READ = 1; // 0001
const WRITE = 2; // 0010
const EXECUTE = 4; // 0100
const permissions = READ | WRITE;
console.log(permissions); // 3The result contains both the read and write bits, so a single number stores multiple states.
Example 2: Bitwise AND checks a flag
Bitwise AND is often used to test whether a specific flag exists in a combined value.
const canWrite = (permissions & WRITE) !== 0;
console.log(canWrite); // trueIf the bit is present, the result is nonzero. If not, the result is 0.
Example 3: Bitwise XOR toggles bits
XOR returns 1 for bits that are different and 0 for bits that match. This makes it useful for toggling a flag.
let mode = 1; // 0001
mode = mode ^ 1; // toggles the lowest bit
console.log(mode); // 0If the same bit is applied twice, it flips on and then off again.
Example 4: Unsigned right shift produces a non-negative result
The unsigned right shift operator fills the left side with zeros. That matters when the number would otherwise keep its sign bit.
const signed = -8;
const unsigned = signed >>> 1;
console.log(unsigned); // a large positive 32-bit valueThis operator is often used when you want to treat the value as an unsigned 32-bit number.
5. Practical Use Cases
- Storing UI state flags such as visible, disabled, selected, or archived in one number.
- Checking file or protocol flags in low-level data handling.
- Working with canvas, image, color, or binary data where compact numeric representation matters.
- Fast integer truncation with value >> 0 or value | 0, though these approaches have limitations.
- Hashing and bit masking in algorithms where binary operations are natural.
For general application code, use bitwise operators only when they improve clarity or solve a real bit-level problem.
6. Common Mistakes
Mistake 1: Using logical operators instead of bitwise operators
Beginners often mix up && and &, or || and |. They look similar but behave very differently.
Problem: Logical operators return truthy or falsy values and short-circuit, while bitwise operators always evaluate numeric bits. Using the wrong one gives the wrong result.
const a = 5;
const b = 3;
const wrong = a && b;Fix: Use the bitwise operator when you need bit-level math.
const correct = a & b;The corrected version works because it compares the individual bits instead of boolean truthiness.
Mistake 2: Expecting normal decimals after a bitwise operation
Bitwise operations convert the value to a 32-bit signed integer first. That means decimals are truncated and large numbers can change unexpectedly.
Problem: A value like 12.9 does not stay decimal after a bitwise operation, so the result may surprise you.
const value = 12.9;
const wrong = value | 0;Fix: Use Math.trunc() when you want to remove the fractional part without relying on bitwise conversion.
const correct = Math.trunc(value);The corrected version is clearer and avoids the 32-bit conversion side effect.
Mistake 3: Confusing signed and unsigned right shift
Right shift has two forms, and they do not behave the same for negative numbers. Using the wrong one can keep the sign when you expected a positive value.
Problem: >> preserves the sign bit, so negative values stay negative after shifting.
const value = -16;
const wrong = value >> 1;Fix: Use >>> when you need zero-fill behavior and an unsigned 32-bit result.
const correct = value >>> 1;The corrected version works because it shifts in zeros from the left instead of copying the sign bit.
7. Best Practices
Use named constants for flags
Named constants make bit masks easier to read and reduce mistakes when multiple bits are involved.
const STATUS_ACTIVE = 1 << 0;
const STATUS_PENDING = 1 << 1;
const STATUS_ARCHIVED = 1 << 2;This is clearer than hardcoding magic numbers like 1, 2, and 4 throughout your code.
Use explicit checks when reading flags
Reading a bitmask is easier to understand when you compare the result to zero.
const isActive = (status & STATUS_ACTIVE) !== 0;The explicit comparison makes the boolean intent obvious to the next person reading the code.
Avoid bitwise tricks when simpler code is clearer
Some developers use bitwise operators for rounding or coercion, but modern JavaScript usually has better options.
const roundedDown = Math.trunc(price);This is usually easier to understand than a coercion trick like price | 0, especially in application code.
8. Limitations and Edge Cases
- Bitwise operators use 32-bit signed integers, so values outside that range are converted and may lose precision.
- Fractions are discarded during conversion, which can change results for decimals.
- Very large numbers can wrap or appear negative because of the sign bit.
- >> and >>> differ only in how they fill the left side, which becomes important for negative numbers.
- Bitwise operators are not a good choice for BigInt values; BigInt has its own bitwise support with different rules.
- Bitwise operations can make code harder to read if the problem is not naturally binary.
Warning: Do not rely on bitwise coercion for financial math or any case where precision matters. It can silently change the number.
9. Practical Mini Project
Let's build a small permission checker using bit flags. This pattern is realistic and shows how bitwise operators help compact several booleans into one value.
const PERM_READ = 1 << 0;
const PERM_WRITE = 1 << 1;
const PERM_DELETE = 1 << 2;
function hasPermission(userPermissions, permission) {
return (userPermissions & permission) !== 0;
}
function addPermission(userPermissions, permission) {
return userPermissions | permission;
}
function removePermission(userPermissions, permission) {
return userPermissions & ~permission;
}
let userPermissions = PERM_READ | PERM_WRITE;
console.log(hasPermission(userPermissions, PERM_READ)); // true
console.log(hasPermission(userPermissions, PERM_DELETE)); // false
userPermissions = addPermission(userPermissions, PERM_DELETE);
console.log(hasPermission(userPermissions, PERM_DELETE)); // true
userPermissions = removePermission(userPermissions, PERM_WRITE);
console.log(hasPermission(userPermissions, PERM_WRITE)); // falseThis mini project demonstrates the standard flag pattern: define bit constants, combine them with OR, test them with AND, and clear them with AND plus NOT.
10. Key Points
- Bitwise operators work on 32-bit signed integers in JavaScript.
- &, |, ^, and ~ operate on bits directly.
- <<, >>, and >>> shift bits and are commonly used with masks and flags.
- Bitwise operators are best for compact flags, masking, and low-level numeric tasks.
- They can make code harder to read if you use them where plain JavaScript would be clearer.
11. Practice Exercise
- Create three flags named CAN_VIEW, CAN_EDIT, and CAN_SHARE.
- Combine CAN_VIEW and CAN_EDIT into one value.
- Write a function that checks whether a given flag is enabled.
- Write a function that removes CAN_EDIT from the combined value.
Expected output: The check function should report that view is enabled, edit is enabled before removal, and edit is disabled after removal.
Hint: Use | to combine flags, & to test, and & ~ to clear a flag.
Solution:
const CAN_VIEW = 1 << 0;
const CAN_EDIT = 1 << 1;
const CAN_SHARE = 1 << 2;
let features = CAN_VIEW | CAN_EDIT;
function hasFlag(value, flag) {
return (value & flag) !== 0;
}
function removeFlag(value, flag) {
return value & ~flag;
}
console.log(hasFlag(features, CAN_VIEW)); // true
console.log(hasFlag(features, CAN_EDIT)); // true
features = removeFlag(features, CAN_EDIT);
console.log(hasFlag(features, CAN_EDIT)); // false
console.log(hasFlag(features, CAN_VIEW)); // trueThe solution uses the standard bit flag pattern and shows how to set, test, and clear bits in a readable way.
12. Final Summary
JavaScript bitwise and shift operators are specialized tools for working with numbers at the binary level. They are most useful when you need compact flags, masks, or low-level numeric manipulation, and they are less appropriate for ordinary application logic.
Remember that JavaScript converts values to 32-bit signed integers for these operators, which explains many of the surprising results beginners see. Once you understand the behavior of &, |, ^, ~, <<, >>, and >>>, you can use them confidently when the problem is truly bit-oriented.
If you want to go further, next learn JavaScript binary number representation and bit masks, since those topics make bitwise code much easier to read and reason about.