All JS Operators in one guide

JS operators are the symbols and keywords that let you do something meaningful with values: add two numbers, compare a user’s input, check if an object has a property or assign a variable its first non-null value. Every expression in your code, from a simple let x = 5 to a complex conditional chain, runs on operators.

This guide covers every category of JavaScript operator with a focused example for each one, plus the precedence rules, common mistakes and modern operators you’re likely to see in real codebases today.


What is a JavaScript operator?

An operator acts on one or more values called operands and produces a result. The expression 3 + 4 uses the + operator on two number operands and resolves to 7. The expression typeof x uses the typeof operator on one operand and returns a string.

Operators fall into three arities based on how many operands they take:

  • Binary takes two operands, one on each side: a + b, x === y
  • Unary takes a single operand, before or after: typeof x, x++
  • Ternary takes three operands: condition ? a : b

All binary operators in JavaScript are infix (the operator sits between operands). Unary operators are usually prefix (!flag, typeof x) except for ++ and -- in postfix position.

Working with JavaScript variables first makes operators much easier to reason about, since every operator works on the values those variables hold.


Arithmetic operators in JavaScript

Arithmetic operators perform mathematical calculations on numbers. JavaScript has eight of them.

Addition (+)

Adds two numbers together. When either operand is a string, + switches to concatenation instead, which is a common source of type bugs.

console.log(5 + 3);   // 8
console.log(1.5 + 2); // 3.5
console.log(5 + "3"); // "53" (string concatenation, not addition)

Subtraction (-)

Subtracts the right operand from the left. If an operand is a string that looks numeric, JavaScript coerces it first.

console.log(10 - 3);   // 7
console.log("9" - 4);  // 5 ("9" coerced to 9)

Multiplication (*)

Multiplies two values.

console.log(4 * 3);  // 12
console.log(2.5 * 4); // 10

Division (/)

Divides the left operand by the right. Dividing by zero produces Infinity, not an error.

console.log(10 / 2); // 5
console.log(7 / 0);  // Infinity

Remainder (%)

Returns what’s left after integer division. Often called the modulo operator. Practical for checking even/odd or wrapping index values.

console.log(10 % 3); // 1
console.log(8 % 2);  // 0 (even number)
console.log(7 % 2);  // 1 (odd number)

Exponentiation (**)

Raises the left operand to the power of the right. Added in ES2016, it replaces Math.pow() for most uses.

console.log(2 ** 8);  // 256
console.log(10 ** 3); // 1000

Increment (++)

Increases a variable’s value by 1. Prefix (++x) increments before the expression evaluates. Postfix (x++) increments after.

let x = 5;
console.log(x++); // 5 (returns current value, then increments)
console.log(x);   // 6

let y = 5;
console.log(++y); // 6 (increments first, then returns)

The prefix vs postfix distinction matters when you mix increment with other operations in the same expression.

Decrement (–)

Decreases a variable’s value by 1. Same prefix/postfix rules as increment.

let n = 10;
n--;
console.log(n); // 9

Assignment operators in JavaScript

Assignment operators set or update a variable’s value. The basic form is = and the compound forms combine assignment with an arithmetic (or bitwise) operation.

Basic assignment (=)

Assigns the right-hand value to the left-hand variable.

let score = 100;
let message = "done";

Compound assignment (+=, -=, *=, /=, %=, **=)

Each compound form is shorthand: x += n means x = x + n. They save repetition and make intent clearer.

let x = 10;
x += 5;  // x = 15
x -= 3;  // x = 12
x *= 2;  // x = 24
x /= 4;  // x = 6
x %= 4;  // x = 2
x **= 3; // x = 8
console.log(x); // 8

Logical assignment (&&=, ||=, ??=)

These ES2021 operators combine a logical test with assignment and only update the variable if the condition is met.

let a = 1;
a &&= 99; // a is truthy, so a = 99
console.log(a); // 99

let b = 0;
b ||= 42; // b is falsy, so b = 42
console.log(b); // 42

let c = null;
c ??= "default"; // c is null, so c = "default"
console.log(c);  // "default"

The ??= form is especially useful for setting a default only when a variable is null or undefined, without overwriting legitimate falsy values like 0 or "".


Comparison operators in JavaScript

Comparison operators compare two operands and return a boolean.

Loose equality (==)

Checks whether two values are equal after type coercion. JavaScript converts operand types before comparing.

console.log(5 == "5");   // true (number coerced to string)
console.log(null == undefined); // true

Strict equality (===)

Checks value and type. No coercion happens. Prefer === in almost every situation because loose equality produces counterintuitive results.

console.log(5 === "5");  // false (different types)
console.log(5 === 5);    // true

Loose inequality (!=)

Returns true if the values are not equal after coercion.

console.log(5 != "5");  // false (coercion makes them equal)

Strict inequality (!==)

Returns true if value or type differ. The safe counterpart to ===.

console.log(5 !== "5"); // true
console.log(5 !== 5);   // false

Greater than (>) and greater than or equal (>=)

console.log(10 > 5);  // true
console.log(5 >= 5);  // true

Less than (<) and less than or equal (<=)

console.log(3 &lt; 7);   // true
console.log(7 &lt;= 7);  // true

For string comparisons these operators compare Unicode code points character by character, which means "banana" < "cherry" is true but "Banana" < "banana" is also true because uppercase letters have lower code points.


Logical operators in JavaScript

Logical operators work on boolean values but return the actual operand value, not just true or false. This makes them more powerful than they first appear.

Logical AND (&&)

Returns the first falsy operand it finds, or the last operand if all are truthy.

console.log(true &amp;&amp; "hello"); // "hello"
console.log(false &amp;&amp; "hello"); // false
console.log(1 &amp;&amp; 2 &amp;&amp; 3);    // 3 (all truthy, returns last)

In conditions: if (user && user.isAdmin) safely guards against a null user before checking the property.

Logical OR (||)

Returns the first truthy operand it finds, or the last operand if all are falsy.

console.log(null || "fallback"); // "fallback"
console.log("value" || "fallback"); // "value"

A common pattern is const name = input || "Anonymous" but this has a flaw: it replaces 0 and "" too, since those are falsy. The nullish coalescing operator fixes that.

Logical NOT (!)

Inverts a boolean. Applying !! to any value converts it to its boolean equivalent.

console.log(!true);   // false
console.log(!0);      // true
console.log(!!"");    // false (empty string is falsy)
console.log(!!1);     // true

Nullish coalescing (??)

Returns the right operand only when the left is null or undefined. Unlike ||, it doesn’t treat 0, "" or false as missing values.

const port = userConfig.port ?? 3000;
// port gets 3000 only if userConfig.port is null or undefined
// a value of 0 is kept as-is

const label = "" ?? "default"; // "" (not replaced)
const label2 = "" || "default"; // "default" (replaced by ||)

For a deeper look at why null and undefined behave differently from other falsy values, see our article on JavaScript null vs undefined.

Short-circuit evaluation

Both && and || stop evaluating as soon as the result is determined. This is called short-circuit evaluation and you can use it to conditionally run code.

const isLoggedIn = true;
isLoggedIn &amp;&amp; console.log("Welcome back"); // logs "Welcome back"

const user = null;
user &amp;&amp; console.log(user.name); // nothing (user is falsy, stops there)

Bitwise operators in JavaScript

Bitwise operators treat their operands as 32-bit integers and operate on their binary representations. They’re rarely needed in application code but appear in graphics, cryptography, permission systems and performance-sensitive number crunching.

Bitwise AND (&)

Returns a 1 in each bit position where both operands have a 1.

console.log(5 &amp; 3); // 1
// 5 = 0101
// 3 = 0011
// &amp;  = 0001

Bitwise OR (|)

Returns a 1 where either operand has a 1.

console.log(5 | 3); // 7
// 0101 | 0011 = 0111

Bitwise XOR (^)

Returns a 1 where operands differ in that bit position.

console.log(5 ^ 3); // 6
// 0101 ^ 0011 = 0110

Bitwise NOT (~)

Inverts all bits. For integer n, the result is -(n + 1).

console.log(~5); // -6

Left shift (<<)

Shifts bits left, filling new positions with 0. Equivalent to multiplying by powers of 2.

console.log(1 &lt;&lt; 3); // 8 (same as 1 * 2^3)

Sign-propagating right shift (>>)

Shifts bits right, preserving the sign bit.

console.log(8 >> 2);  // 2 (same as Math.floor(8 / 4))
console.log(-8 >> 2); // -2

Zero-fill right shift (>>>)

Shifts right, always filling with 0 regardless of sign. Useful for converting negative numbers to unsigned 32-bit values.

console.log(-1 >>> 0); // 4294967295 (max unsigned 32-bit integer)

String operators in JavaScript

The + and += operators have a second role when used with strings.

Concatenation (+)

Joins two strings. If one operand is a string and the other is not, JavaScript converts the non-string to a string first.

const greeting = "Hello" + " " + "World";
console.log(greeting); // "Hello World"

console.log("Score: " + 100); // "Score: 100"
console.log(1 + 2 + "3");    // "33" (left to right: 1+2=3, then "3"+"3"="33")

Concatenation assignment (+=)

Appends a string to an existing variable.

let message = "Hello";
message += ", World";
console.log(message); // "Hello, World"

In modern code, template literals often replace string concatenation for readability: `Score: ${score}` instead of "Score: " + score. But + and += are still common in loops and performance-sensitive code where template literal overhead matters.


Ternary operator in JavaScript

The ternary operator is the only three-operand operator in JavaScript. It’s a compact inline if/else.

condition ? valueIfTrue : valueIfFalse
const age = 20;
const status = age >= 18 ? "adult" : "minor";
console.log(status); // "adult"

It’s well-suited for simple two-branch decisions directly inside an assignment or a JSX expression. Avoid nesting ternaries because the moment you have a ternary inside another ternary, an if/else statement is almost always clearer.

// readable
const label = score >= 90 ? "A" : "not A";

// hard to maintain, use if/else instead
const grade = score >= 90 ? "A" : score >= 80 ? "B" : score >= 70 ? "C" : "F";

Unary operators in JavaScript

Unary operators take a single operand. They cover type inspection, deletion and numeric conversion.

typeof

Returns a string describing the type of a value. Useful for runtime type guards.

console.log(typeof 42);        // "number"
console.log(typeof "hello");   // "string"
console.log(typeof true);      // "boolean"
console.log(typeof undefined); // "undefined"
console.log(typeof null);      // "object" (historical JavaScript quirk)
console.log(typeof {});        // "object"
console.log(typeof []);        // "object" (arrays are objects)
console.log(typeof function(){}); // "function"

The null case returns "object" due to a bug in JavaScript’s first release that was never fixed to avoid breaking the web. For a thorough breakdown of object type checking, see how to check the type of any object in JavaScript.

delete

Removes a property from an object. Returns true on success.

const user = { name: "Alex", role: "admin" };
delete user.role;
console.log(user); // { name: "Alex" }

delete does not work on variables declared with var, let or const.

void

Evaluates an expression and returns undefined. Most commonly seen as void 0 as a reliable way to get undefined (since undefined was reassignable in older JavaScript) or in <a href="javascript:void(0)"> links.

console.log(void 0);        // undefined
console.log(void "hello");  // undefined

Unary plus (+) and negation (-)

Unary + converts its operand to a number. Unary - does the same and negates it.

console.log(+"42");    // 42
console.log(+true);    // 1
console.log(+false);   // 0
console.log(+null);    // 0
console.log(+"hello"); // NaN

console.log(-5);    // -5
console.log(-"10"); // -10

Relational operators in JavaScript

Relational operators test relationships between a value and an object or prototype chain.

in

Checks whether a property name (as a string) exists in an object or its prototype chain.

const config = { host: "localhost", port: 3000 };
console.log("host" in config);    // true
console.log("timeout" in config); // false

// also checks inherited properties
console.log("toString" in config); // true (from Object.prototype)

Use in when you want to confirm a property exists before accessing it, rather than checking the value.

instanceof

Tests whether an object was created by a given constructor, or more precisely whether the constructor’s prototype property appears anywhere in the object’s prototype chain.

const arr = [1, 2, 3];
console.log(arr instanceof Array);  // true
console.log(arr instanceof Object); // true (Array inherits from Object)

class Dog {}
const rex = new Dog();
console.log(rex instanceof Dog); // true

instanceof doesn’t work across different execution contexts (like iframes), because each context has its own Array constructor. For reliable cross-realm checks, use Array.isArray() or Object.prototype.toString.call(). Our guide on JavaScript object prototypes explains why the prototype chain matters here.


Optional chaining (?.) in JavaScript

The optional chaining operator (?.) lets you read a deeply nested property without writing a chain of null checks. If any reference in the chain is null or undefined, the expression short-circuits and returns undefined instead of throwing a TypeError.

const user = {
  profile: {
    address: {
      city: "Mumbai"
    }
  }
};

// Without optional chaining
const city = user &amp;&amp; user.profile &amp;&amp; user.profile.address &amp;&amp; user.profile.address.city;

// With optional chaining
const city2 = user?.profile?.address?.city;
console.log(city2); // "Mumbai"

const zip = user?.profile?.address?.zip;
console.log(zip); // undefined (no error thrown)

It works with method calls and bracket notation too:

const result = someObj?.method?.();
const val = arr?.[0];

Combine ?. with ?? to provide a fallback:

const city = user?.profile?.address?.city ?? "Unknown city";

This pattern is standard in any code that works with API responses or user-supplied data, where a property’s existence can’t be guaranteed. The JavaScript arrays article shows this pattern in action when iterating over potentially missing nested array values.


BigInt operators in JavaScript

BigInt values (written with an n suffix) support all standard arithmetic and bitwise operators, but you cannot mix BigInt with regular number operands in the same expression.

const big = 9007199254740991n + 1n;
console.log(big); // 9007199254740992n

// This throws a TypeError:
// console.log(9007199254740991n + 1);

The unsigned right shift operator (>>>) is not supported for BigInt.


Comma operator in JavaScript

The comma operator evaluates each operand from left to right and returns the value of the last one. It appears mainly in for loop initialization and update expressions.

for (let i = 0, j = 10; i &lt; 5; i++, j--) {
  console.log(i, j);
}

// As a standalone expression (uncommon in application code)
const result = (1, 2, 3);
console.log(result); // 3

Outside of for loops, you rarely need the comma operator directly. It’s more of a syntax feature than a tool you reach for deliberately.


Operator precedence in JavaScript

When an expression contains multiple operators, JavaScript evaluates them in a defined order called operator precedence. Higher-precedence operators evaluate first.

console.log(2 + 3 * 4);   // 14 (multiplication before addition)
console.log((2 + 3) * 4); // 20 (parentheses override precedence)

A condensed precedence order (high to low):

  1. Grouping ()
  2. Member access ., ?., []
  3. Function call ()
  4. Postfix ++, --
  5. Unary: !, ~, +, -, ++, --, typeof, void, delete
  6. Exponentiation **
  7. Multiplicative: *, /, %
  8. Additive: +, -
  9. Shift: <<, >>, >>>
  10. Relational: <, >, <=, >=, in, instanceof
  11. Equality: ==, !=, ===, !==
  12. Bitwise: &, ^, |
  13. Logical: &&, ||, ??
  14. Ternary ?:
  15. Assignment: =, +=, -=, etc.
  16. Comma ,

When in doubt, use parentheses. They cost nothing and remove all ambiguity.


Common mistakes with JS operators

Using = instead of === in conditions

Writing if (x = 5) assigns 5 to x and always evaluates to truthy. This is a bug that’s painful to find.

// Bug: always true, and modifies x
if (x = 5) { }

// Correct: strict comparison
if (x === 5) { }

Relying on == for null checks

Loose equality == treats null and undefined as equal to each other and nothing else. That’s actually one place where == is intentionally useful:

// Both null and undefined pass this check
if (value == null) { console.log("no value"); }

// Equivalent but more verbose
if (value === null || value === undefined) { }

Use == null intentionally here, not by accident.

Mixing + with numbers and strings

The + operator checks operands left to right. Once it hits a string, everything after becomes string concatenation.

console.log(1 + 2 + "3");  // "33"
console.log("1" + 2 + 3);  // "123"

Wrap number inputs with Number() or the unary + when concatenating with user data.

Forgetting typeof returns a string

typeof x === "number" is correct. typeof x === number is a ReferenceError if number is not defined as a variable.

Using || for defaults when 0 or “” are valid

// Bug: replaces 0 with the default, even though 0 is a valid port
const port = userPort || 8080;

// Fix: use ?? instead
const port2 = userPort ?? 8080;

Key takeaways

  • JS operators act on operands: binary takes two, unary takes one and ternary takes three
  • Use === and !== over == and != to avoid type coercion surprises
  • ?? replaces || for default values when 0, "" and false are valid inputs
  • ?. short-circuits on null or undefined and avoids TypeError in property chains
  • typeof returns a string and null returns "object" due to a historical bug
  • Arithmetic + doubles as string concatenation so coerce operands explicitly when mixing types
  • Bitwise operators treat values as 32-bit integers and are rarely needed in application code
  • Parentheses override all precedence rules and make intent unambiguous

Frequently asked questions (FAQs)

What are JS operators?

JS operators are symbols or keywords that act on one or more values to produce a result, such as adding numbers, comparing values or assigning data to a variable.

What is the difference between == and === in JavaScript?

== compares values after type coercion while === compares value and type without coercion. Use === by default to avoid unexpected matches.

What does the nullish coalescing operator ?? do?

It returns the right operand only when the left is null or undefined, making it safer than || for providing defaults when falsy values like 0 or "" are valid.

What is operator precedence in JavaScript?

Operator precedence determines the order in which operators are evaluated in an expression. Multiplication runs before addition and parentheses override the default order.

What does typeof return for null in JavaScript?

typeof null returns "object" due to a bug in JavaScript’s original implementation that was never corrected to avoid breaking existing code.

How does optional chaining (?.) work in JavaScript?

It accesses nested properties and short-circuits to undefined if any reference in the chain is null or undefined, preventing a TypeError.

When should I use bitwise operators in JavaScript?

Use them for low-level operations like flag systems, color manipulation or performance-sensitive arithmetic where working directly with binary representations is intentional.


Final conclusion

JavaScript operators are the foundation that everything else (if/else logic, loops, function calls and object access) runs on top of. Getting the distinctions right, especially == vs ===, || vs ?? and the prefix vs postfix forms of ++ and --, will save you a lot of debugging time in production.

Aditya Gupta
Aditya Gupta
Articles: 512