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.

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:

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); // 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.

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

console.log(shifted); // 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.

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

const permissions = READ | WRITE;

console.log(permissions); // 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.

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

console.log(canWrite); // true

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.

let mode = 1; // 0001

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

console.log(mode); // 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.

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

console.log(unsigned); // a large positive 32-bit value

This operator is often used when you want to treat the value as an unsigned 32-bit number.

5. Practical Use Cases

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

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)); // 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

11. Practice Exercise

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)); // 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.