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

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.

JavaScript
const a = 5; // 0101 in binary
const b = 3; // 0011 in binary

const result = a & b; // 0001 in binary

console.log(result); // 1
Output✓ Tested · Node 24
1

Here, 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.

JavaScript
const value = 6; // 0110
const shifted = value << 1; // 1100

console.log(shifted); // 12
Output✓ Tested · Node 24
12

This 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.

JavaScript
const READ = 1;   // 0001
const WRITE = 2;  // 0010
const EXECUTE = 4; // 0100

const permissions = READ | WRITE;

console.log(permissions); // 3
Output✓ Tested · Node 24
3

The 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.

JavaScript
const canWrite = (permissions & WRITE) !== 0;

console.log(canWrite); // true
Output✓ Tested · Node 24
Uncaught ReferenceError: permissions is not defined

If 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.

JavaScript
let mode = 1; // 0001

mode = mode ^ 1; // toggles the lowest bit

console.log(mode); // 0
Output✓ Tested · Node 24
0

If 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.

JavaScript
const signed = -8;
const unsigned = signed >>> 1;

console.log(unsigned); // a large positive 32-bit value
Output✓ Tested · Node 24
2147483644

This 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.

JavaScript
const a = 5;
const b = 3;

const wrong = a && b;

Fix: Use the bitwise operator when you need bit-level math.

JavaScript
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.

JavaScript
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.

JavaScript
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.

JavaScript
const value = -16;
const wrong = value >> 1;

Fix: Use >>> when you need zero-fill behavior and an unsigned 32-bit result.

JavaScript
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.

JavaScript
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.

JavaScript
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.

JavaScript
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.

JavaScript
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)); // false
Output✓ Tested · Node 24
true
false
true
false

This 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.

Show solution
JavaScript
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)); // true
Output✓ Tested · Node 24
true
true
false
true

The 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.