Arithmetic Operators
Arithmetic operators perform mathematical calculations on numbers.
let a = 10, b = 3;
console.log(a + b); // 13 β addition
console.log(a - b); // 7 β subtraction
console.log(a * b); // 30 β multiplication
console.log(a / b); // 3.333... β division
console.log(a % b); // 1 β remainder (modulo)
console.log(a ** b); // 1000 β exponentiation (10Β³)
// Increment and decrement
let x = 5;
console.log(x++); // 5 β post-increment: returns THEN adds
console.log(x); // 6
console.log(++x); // 7 β pre-increment: adds THEN returns
console.log(x--); // 7 β post-decrement: returns THEN subtracts
console.log(x); // 6
The increment/decrement operators (++/--) have a confusing pre/post distinction. Many style guides recommend using += 1 instead for clarity β it's always obvious what it does.
Assignment Operators
Assignment operators store values in variables. The compound assignment operators combine an arithmetic operation with assignment.
let n = 10;
n += 5; console.log(n); // 15 β same as n = n + 5
n -= 3; console.log(n); // 12 β same as n = n - 3
n *= 2; console.log(n); // 24 β same as n = n * 2
n /= 4; console.log(n); // 6 β same as n = n / 4
n %= 4; console.log(n); // 2 β same as n = n % 4
n **= 3; console.log(n); // 8 β same as n = n ** 3
// Logical assignment (ES2021)
let user = null;
user ??= "Guest"; // assigns only if null or undefined
console.log(user); // "Guest"
Comparison Operators
Comparison operators compare two values and return a boolean (true or false).
console.log(5 > 3); // true
console.log(5 < 3); // false
console.log(5 >= 5); // true
console.log(5 <= 4); // false
// == (loose equality) β performs type coercion
console.log(5 == "5"); // true β string coerced to number
console.log(0 == false); // true β false coerced to 0
console.log(null == undefined); // true
// === (strict equality) β NO type coercion
console.log(5 === "5"); // false β different types
console.log(0 === false); // false β different types
console.log(null === undefined); // false
// != and !==
console.log(5 != "5"); // false β loose: they're equal after coercion
console.log(5 !== "5"); // true β strict: different types
Loose equality (==) applies type coercion in surprising ways. 0 == "" is true, "0" == false is true, and [] == 0 is true. These are trap cases. Use === always β it checks both value and type without any conversions.
Logical Operators
Logical operators work with boolean values, but in JavaScript they have an important extra behavior β they return one of their operands, not just true/false.
// && (AND) β returns first falsy value, or last value if all truthy
console.log(true && true); // true
console.log(true && false); // false
console.log("hello" && 42); // 42 (both truthy, returns last)
console.log(0 && "hello"); // 0 (0 is falsy, short-circuits)
// || (OR) β returns first truthy value, or last value if all falsy
console.log(false || true); // true
console.log(0 || "default"); // "default"
console.log("user" || "anonymous"); // "user"
// ! (NOT) β negates the boolean value
console.log(!true); // false
console.log(!0); // true
console.log(!""); // true
Nullish Coalescing (??) and Optional Chaining (?.)
These modern operators (ES2020) make working with null and undefined values much safer and cleaner.
// ?? returns right side only if left is null or undefined
// (unlike || which triggers for ALL falsy values like 0 and "")
let count = 0;
console.log(count || 10); // 10 β 0 is falsy, so || uses right side
console.log(count ?? 10); // 0 β 0 is not null/undefined, so ?? keeps it
let name = null;
console.log(name ?? "Anonymous"); // "Anonymous"
// ?. (optional chaining) β safely access nested properties
const user = { profile: { name: "Alice" } };
const noUser = null;
console.log(user?.profile?.name); // "Alice"
console.log(noUser?.profile?.name); // undefined (no error!)
// Useful with methods too
console.log(user?.greet?.()); // undefined (no error if greet doesn't exist)
Ternary Operator
The ternary operator is a concise way to write an if/else expression in a single line: condition ? valueIfTrue : valueIfFalse.
const age = 20;
const status = age >= 18 ? "adult" : "minor";
console.log(status); // "adult"
// Nested ternary (use sparingly β can hurt readability)
const score = 75;
const grade = score >= 90 ? "A"
: score >= 80 ? "B"
: score >= 70 ? "C"
: "F";
console.log(grade); // "C"
Operator Precedence
When an expression has multiple operators, JavaScript evaluates them in a specific order. Higher precedence operators bind more tightly.
| Precedence | Operators | Description |
|---|---|---|
| Highest | () | Grouping |
| 18 | ?. | Optional chaining |
| 17 | ! ++ -- | Unary operators |
| 16 | ** | Exponentiation (right to left) |
| 15 | * / % | Multiplicative |
| 14 | + - | Additive |
| 12 | < > <= >= | Relational |
| 11 | == === != !== | Equality |
| 6 | && | Logical AND |
| 5 | || | Logical OR |
| 4 | ?? | Nullish coalescing |
| 3 | ? : | Ternary |
| Lowest | = += etc. | Assignment |
You don't need to memorize every precedence level. Use parentheses () to make the order of operations explicit. (a + b) * c is clearer than relying on the reader to know multiplication comes before addition.
ποΈ Practical Exercise
- Compute
17 % 5,2 ** 8, andMath.floor(17 / 5)in the console. - Test the difference between
0 == falseand0 === false. Then test"" == falseand"" === false. - Use
||to provide a default value:const username = "" || "Guest". Then replace||with??and notice the different result. - Use optional chaining to safely access
user.address.citywhereusermight benull. - Rewrite an if/else statement that assigns "pass" or "fail" using the ternary operator.
π₯ Challenge Exercise
Write a function calculateBMI(weight, height) that computes BMI (weight in kg divided by height in meters squared). Use the ternary operator (or chained ternary) to categorize the result as "Underweight" (BMI < 18.5), "Normal" (18.5β24.9), "Overweight" (25β29.9), or "Obese" (30+). Return an object with both the bmi value (rounded to 1 decimal) and the category string.
π Summary
- Arithmetic operators:
+,-,*,/,%,**,++,-- - Always use
===(strict equality) instead of==to avoid type coercion surprises. &&and||return operands, not just booleans β useful for short-circuit evaluation and default values.??(nullish coalescing) is safer than||when0or""are valid values.?.(optional chaining) safely accesses nested properties without throwing on null/undefined.- Use parentheses to make operator precedence explicit and code readable.
Interview Questions
- What is the difference between
==and===in JavaScript? - What does the
??operator do differently than||? - Explain short-circuit evaluation in JavaScript.
- What is optional chaining (
?.) and when would you use it? - What is the result of
1 + "2"and"3" - 1?
Frequently Asked Questions
The result is the string "12". When the + operator encounters a string operand, it treats the other operand as a string too and concatenates. However, "3" - 1 gives 2 because - only does subtraction and coerces the string "3" to the number 3.
JavaScript stops evaluating logical expressions as soon as the outcome is determined. With &&, if the left side is falsy, the right side is never evaluated. With ||, if the left side is truthy, the right side is skipped. This is useful for conditional execution: user && sendEmail(user) only calls sendEmail if user is truthy.
typeof returns a string representing the type of its operand: "string", "number", "bigint", "boolean", "undefined", "symbol", "object", or "function". Remember: typeof null === "object" (historical bug) and typeof [] === "object" (use Array.isArray() instead).
Yes! Use obj.method?.() β the ?. before () checks if method exists before calling it. Without optional chaining, calling a non-existent method throws TypeError: obj.method is not a function.
Use the ternary for simple, single-expression conditions where you're choosing between two values: const label = count === 1 ? "item" : "items". Use if/else for multi-statement blocks, side effects, or when readability suffers. Avoid deeply nested ternaries β they're notoriously hard to read.