Operators and Expressions Reference

1. Arithmetic Operators and Math Operations

Operator Name Description Example Result
+ Addition / Unary Plus Adds numbers or concatenates strings; converts to number 5 + 3, +"42" 8, 42
- Subtraction / Unary Minus Subtracts numbers; negates value 5 - 3, -5 2, -5
* Multiplication Multiplies numbers 5 * 3 15
/ Division Divides numbers (floating-point result) 10 / 3 3.3333...
% Remainder / Modulo Returns remainder of division 10 % 3 1
** Exponentiation ES2016 Raises to power 2 ** 3 8
++ Increment Increases by 1 (prefix or postfix) ++x, x++ Pre: return new; Post: return old
-- Decrement Decreases by 1 (prefix or postfix) --x, x-- Pre: return new; Post: return old
Operation Type Behavior Special Cases
Number + Number Numeric addition 5 + 3 = 8
String + Any String concatenation "5" + 3 = "53", "a" + true = "atrue"
Division by Zero Returns Infinity 5 / 0 = Infinity, -5 / 0 = -Infinity
0 / 0 Returns NaN Indeterminate result
NaN in Operations Propagates NaN NaN + 5 = NaN, NaN * 2 = NaN
Infinity Operations Mathematical infinity rules Infinity + 1 = Infinity, Infinity * -1 = -Infinity

Example: Arithmetic operators and edge cases

// Basic arithmetic
console.log(10 + 5);   // 15
console.log(10 - 5);   // 5
console.log(10 * 5);   // 50
console.log(10 / 5);   // 2
console.log(10 % 3);   // 1 (remainder)
console.log(2 ** 3);   // 8 (exponentiation)

// Increment/Decrement
let x = 5;
console.log(x++);      // 5 (returns old value, then increments)
console.log(x);        // 6
console.log(++x);      // 7 (increments, then returns new value)
console.log(x--);      // 7 (returns old value, then decrements)
console.log(--x);      // 5 (decrements, then returns new value)

// String concatenation with +
console.log("Hello" + " " + "World");  // "Hello World"
console.log("5" + 3);    // "53" (string concatenation)
console.log(5 + "3");    // "53"
console.log("5" + 3 + 2); // "532"
console.log(5 + 3 + "2"); // "82" (left to right: 5+3=8, then 8+"2"="82")

// Type coercion with other operators
console.log("10" - 5);   // 5 (string to number)
console.log("10" * "2"); // 20
console.log("10" / "2"); // 5
console.log("10" % 3);   // 1

// Unary operators
console.log(+"42");      // 42 (string to number)
console.log(+"abc");     // NaN
console.log(-"5");       // -5

// Special values
console.log(5 / 0);      // Infinity
console.log(-5 / 0);     // -Infinity
console.log(0 / 0);      // NaN
console.log(NaN + 10);   // NaN (NaN propagates)
console.log(Infinity - 1); // Infinity

2. Assignment Operators and Compound Assignment

Operator Name Equivalent Example Result
= Simple Assignment N/A x = 5 x is 5
+= Addition Assignment x = x + y x += 3 Adds and assigns
-= Subtraction Assignment x = x - y x -= 3 Subtracts and assigns
*= Multiplication Assignment x = x * y x *= 3 Multiplies and assigns
/= Division Assignment x = x / y x /= 3 Divides and assigns
%= Remainder Assignment x = x % y x %= 3 Remainder and assigns
**= Exponentiation Assignment x = x ** y x **= 3 Exponentiates and assigns
<<= Left Shift Assignment x = x << y x <<= 2 Left shift and assigns
>>= Right Shift Assignment x = x >> y x >>= 2 Right shift and assigns
>>>= Unsigned Right Shift Assignment x = x >>> y x >>>= 2 Unsigned shift and assigns
&= Bitwise AND Assignment x = x & y x &= 3 AND and assigns
|= Bitwise OR Assignment x = x | y x |= 3 OR and assigns
^= Bitwise XOR Assignment x = x ^ y x |= 3 XOR and assigns
Feature Description Example
Destructuring Assignment Unpack values from arrays/objects [a, b] = [1, 2], {x, y} = obj
Multiple Assignment Chain assignments (right-to-left) a = b = c = 5 (all get 5)
Return Value Assignment returns assigned value y = (x = 5) (both are 5)
Compound Benefits Shorter, clearer, evaluates left side once arr[i++] += 5 increments i once

Example: Assignment operators

// Simple assignment
let x = 10;

// Compound assignments
x += 5;   // x = x + 5;  x is now 15
x -= 3;   // x = x - 3;  x is now 12
x *= 2;   // x = x * 2;  x is now 24
x /= 4;   // x = x / 4;  x is now 6
x %= 5;   // x = x % 5;  x is now 1
x **= 3;  // x = x ** 3; x is now 1

// String concatenation with +=
let str = "Hello";
str += " World";  // str is "Hello World"

// Multiple assignment (right-to-left)
let a, b, c;
a = b = c = 5;  // All are 5
console.log(a, b, c);  // 5 5 5

// Assignment returns value
let y = (x = 10);  // x is 10, y is 10
console.log(x, y);  // 10 10

// Destructuring assignment
let [p, q] = [1, 2];
console.log(p, q);  // 1 2

let {name, age} = {name: "John", age: 30};
console.log(name, age);  // "John" 30

// Compound assignment with side effects
let arr = [10, 20, 30];
let i = 0;
arr[i++] += 5;  // arr[0] becomes 15, then i increments
console.log(arr[0], i);  // 15 1

// Swapping with destructuring
let m = 1, n = 2;
[m, n] = [n, m];  // Swap
console.log(m, n);  // 2 1

3. Comparison Operators and Equality Rules

Operator Name Description Type Coercion Example
== Equality (loose) Checks value equality with type coercion ✓ Yes 5 == "5"true
=== Strict Equality Checks value and type equality ✗ No 5 === "5"false
!= Inequality (loose) Checks value inequality with coercion ✓ Yes 5 != "6"true
!== Strict Inequality Checks value or type inequality ✗ No 5 !== "5"true
< Less Than Checks if left < right ✓ Yes 5 < 10true
> Greater Than Checks if left > right ✓ Yes 10 > 5true
<= Less Than or Equal Checks if left ≤ right ✓ Yes 5 <= 5true
>= Greater Than or Equal Checks if left ≥ right ✓ Yes 10 >= 10true
Comparison == (loose) === (strict) Reason
5 vs "5" true false String coerced to number
0 vs false true false Boolean coerced to number
null vs undefined true false Special case: only equal to each other
"" vs 0 true false Empty string coerced to 0
NaN vs NaN false false NaN not equal to itself (use isNaN())
[] vs [] false false Different object references
0 vs -0 true true Considered equal (use Object.is() to distinguish)

Example: Equality and comparison operators

// Strict equality (===) - recommended
console.log(5 === 5);        // true
console.log(5 === "5");      // false (different types)
console.log(null === null);  // true
console.log(undefined === undefined); // true

// Loose equality (==) - with type coercion
console.log(5 == "5");       // true (string coerced to number)
console.log(0 == false);     // true (boolean to number)
console.log("" == 0);        // true (string to number)
console.log(null == undefined); // true (special case)

// Inequality
console.log(5 !== "5");      // true (strict)
console.log(5 != "6");       // true (loose)

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

// String comparison (lexicographic)
console.log("apple" < "banana"); // true (a < b)
console.log("10" < "9");         // true (string comparison: "1" < "9")
console.log(10 < "9");           // false (number comparison: 10 > 9)

// Special cases
console.log(NaN === NaN);     // false (use Number.isNaN())
console.log(NaN == NaN);      // false
console.log(Number.isNaN(NaN)); // true (correct way)

console.log(0 === -0);        // true
console.log(Object.is(0, -0)); // false (distinguishes +0 and -0)

// Object comparison (reference)
const obj1 = {a: 1};
const obj2 = {a: 1};
const obj3 = obj1;
console.log(obj1 === obj2);  // false (different references)
console.log(obj1 === obj3);  // true (same reference)

// Array comparison
console.log([1,2] == [1,2]); // false (different references)
console.log([1,2] === [1,2]); // false

// Tricky comparisons to avoid
console.log([] == ![]);      // true (![] is false, [] coerced to "")
console.log([] == false);    // true
console.log("" == 0);        // true
console.log(" " == 0);       // true
console.log("0" == 0);       // true
console.log("0" == false);   // true
Warning: Always use === and !== (strict equality) to avoid unexpected type coercion bugs. Use == only when you specifically need type coercion.

4. Logical Operators and Short-circuit Evaluation

Operator Name Returns Short-circuits Use Case
&& Logical AND First falsy value or last value ✓ If left is falsy Both conditions must be true
|| Logical OR First truthy value or last value ✓ If left is truthy At least one condition must be true
! Logical NOT Boolean (inverted) ✗ No Inverts truthiness
!! Double NOT Boolean conversion ✗ No Convert to boolean explicitly
Expression Result Explanation
true && true true Both truthy → returns last value
true && false false Right is falsy → returns false
false && true false Short-circuits at false (left)
5 && "hello" "hello" Both truthy → returns last
0 && "hello" 0 First falsy → returns 0
true || false true Short-circuits at first truthy
false || true true Returns first truthy value
false || false false Both falsy → returns last
5 || "hello" 5 First truthy → short-circuits
0 || "hello" "hello" First falsy, returns second
!true false Inverts boolean
!0 true 0 is falsy, inverted to true
!!"hello" true Convert to boolean: truthy

Example: Logical operators and short-circuit evaluation

// Logical AND (&&) - returns first falsy or last value
console.log(true && true);        // true
console.log(true && false);       // false
console.log(5 && 10);             // 10 (both truthy, returns last)
console.log(0 && 10);             // 0 (first falsy)
console.log(null && "hello");     // null (first falsy)

// Logical OR (||) - returns first truthy or last value
console.log(false || true);       // true
console.log(false || false);      // false (last value)
console.log(5 || 10);             // 5 (first truthy)
console.log(0 || 10);             // 10 (first falsy, returns second)
console.log(null || "default");   // "default" (first falsy)

// Logical NOT (!)
console.log(!true);               // false
console.log(!false);              // true
console.log(!0);                  // true (0 is falsy)
console.log(!"");                 // true ("" is falsy)
console.log(!"hello");            // false ("hello" is truthy)

// Double NOT (!!) - convert to boolean
console.log(!!"hello");           // true
console.log(!!0);                 // false
console.log(!!"");                // false
console.log(!!{});                // true (objects are truthy)

// Short-circuit evaluation - practical uses

// 1. Default values (before ?? was added)
let username = input || "Guest";  // If input is falsy, use "Guest"

// 2. Conditional execution
isLoggedIn && showDashboard();    // Execute if truthy
hasError || showSuccessMessage(); // Execute if falsy

// 3. Guard clauses
function processUser(user) {
    user && user.profile && console.log(user.profile.name);
    // Only accesses nested property if all are truthy
}

// 4. Avoiding function calls
let result = expensiveCheck() || cachedValue;
// If expensiveCheck() is truthy, cachedValue never evaluated

// Short-circuit prevents errors
let obj = null;
let value = obj && obj.property;  // undefined (doesn't throw)
// Without short-circuit: obj.property would throw error

// Combining operators
let access = isAdmin || (isPremium && hasPermission);
console.log(true || (false && error())); // true, error() not called

// Falsy values: false, 0, "", null, undefined, NaN
console.log(false || 0 || "" || null || undefined || NaN || "found");
// "found" (first truthy)
Note: Logical operators return the actual value (not just boolean). Short-circuit evaluation stops at first definitive result, useful for default values and conditional execution.

5. Bitwise Operators and Binary Operations

Operator Name Description Example Binary Result
& AND 1 if both bits are 1 5 & 3 0101 & 0011 1 (0001)
| OR 1 if at least one bit is 1 5 | 3 0101 | 0011 7 (0111)
^ XOR 1 if bits are different 5 ^ 3 0101 ^ 0011 6 (0110)
~ NOT Inverts all bits ~5 ~0101 -6 (two's complement)
<< Left Shift Shifts bits left, fills 0s 5 << 1 0101 → 1010 10
>> Sign-propagating Right Shift Shifts bits right, preserves sign 5 >> 1 0101 → 0010 2
>>> Zero-fill Right Shift Shifts bits right, fills 0s -5 >>> 1 Fills with 0s Large positive number
Use Case Operation Example Purpose
Check if even n & 1 5 & 1 = 1 (odd), 4 & 1 = 0 (even) Fast even/odd check
Multiply by 2n n << x 5 << 2 = 20 (5 × 4) Fast multiplication
Divide by 2n n >> x 20 >> 2 = 5 (20 ÷ 4) Fast division
Toggle bit n ^ (1 << i) Toggles i-th bit Flags/permissions
Set bit n | (1 << i) Sets i-th bit to 1 Enable flag
Clear bit n & ~(1 << i) Sets i-th bit to 0 Disable flag
Check bit (n >> i) & 1 Gets value of i-th bit Test flag
Swap variables a ^= b; b ^= a; a ^= b; Swap without temp variable XOR swap trick

Example: Bitwise operations

// Basic bitwise operations
console.log(5 & 3);    // 1  (0101 & 0011 = 0001)
console.log(5 | 3);    // 7  (0101 | 0011 = 0111)
console.log(5 ^ 3);    // 6  (0101 ^ 0011 = 0110)
console.log(~5);       // -6 (inverts bits, two's complement)

// Bit shifts
console.log(5 << 1);   // 10  (0101 → 1010) multiply by 2
console.log(5 << 2);   // 20  (0101 → 10100) multiply by 4
console.log(10 >> 1);  // 5   (1010 → 0101) divide by 2
console.log(20 >> 2);  // 5   (10100 → 0101) divide by 4

// Practical use cases

// 1. Check if number is even or odd
function isEven(n) {
    return (n & 1) === 0;
}
console.log(isEven(4));  // true
console.log(isEven(5));  // false

// 2. Flags and permissions
const READ = 1;      // 001
const WRITE = 2;     // 010
const EXECUTE = 4;   // 100

let permissions = 0;
permissions |= READ;       // Add READ permission
permissions |= WRITE;      // Add WRITE permission

console.log(permissions & READ);    // Check if READ (non-zero = true)
console.log(permissions & EXECUTE); // Check if EXECUTE (0 = false)

permissions &= ~WRITE;     // Remove WRITE permission
console.log(permissions);  // 1 (only READ)

// 3. Fast integer conversion
console.log(~~3.14);       // 3 (double NOT truncates decimals)
console.log(5.7 | 0);      // 5 (OR with 0 truncates)
console.log(5.7 >> 0);     // 5 (right shift by 0 truncates)

// 4. XOR swap (without temp variable)
let a = 5, b = 10;
a ^= b;  // a = 5 ^ 10
b ^= a;  // b = 10 ^ (5 ^ 10) = 5
a ^= b;  // a = (5 ^ 10) ^ 5 = 10
console.log(a, b);  // 10 5

// 5. Toggle boolean
let flag = true;
flag ^= 1;  // false (1 ^ 1 = 0)
flag ^= 1;  // true (0 ^ 1 = 1)

// 6. Set specific bit
let n = 0b0000;  // Binary literal
n |= (1 << 2);   // Set bit 2: 0b0100
console.log(n.toString(2));  // "100"

// 7. Clear specific bit
n &= ~(1 << 2);  // Clear bit 2: 0b0000
console.log(n.toString(2));  // "0"
Note: Bitwise operators work on 32-bit integers. Common uses include flags, permissions, fast math operations, and low-level data manipulation.

6. Optional Chaining (?.) and Nullish Coalescing (??)

Operator Syntax Returns Use Case
?. ES2020 obj?.prop undefined if obj is null/undefined, else obj.prop Safe property access
?.[] obj?.[expr] undefined if obj is null/undefined Safe dynamic property access
?.() func?.(args) undefined if func is null/undefined Safe function call
?? ES2020 a ?? b a if not null/undefined, else b Default values for null/undefined only
Feature || (OR) ?? (Nullish Coalescing)
Falsy values treated as missing Yes (false, 0, "", null, undefined, NaN) No (only null/undefined)
0 || 100 100 0
"" || "default" "default" ""
false || true true false
null ?? 100 100 100
undefined ?? 100 100 100
Use case When any falsy should use default When only null/undefined should use default

Example: Optional chaining and nullish coalescing

// Optional Chaining (?.)

// Without optional chaining (verbose and error-prone)
let user = null;
// let name = user.profile.name; // TypeError: Cannot read property 'profile' of null
let name = user && user.profile && user.profile.name; // undefined

// With optional chaining (concise and safe)
name = user?.profile?.name; // undefined (no error)

const user2 = {
    profile: {
        name: "Alice",
        address: {
            city: "NYC"
        }
    }
};

console.log(user2?.profile?.name);          // "Alice"
console.log(user2?.profile?.address?.city); // "NYC"
console.log(user2?.profile?.phone);         // undefined (no error)
console.log(user2?.account?.balance);       // undefined

// Optional chaining with arrays
const arr = null;
console.log(arr?.[0]);        // undefined (safe)
console.log(user2?.tags?.[0]); // undefined

// Optional chaining with function calls
const obj = {
    method: function() { return "called"; }
};

console.log(obj.method?.());      // "called"
console.log(obj.nonExistent?.());  // undefined (no error)
console.log(user?.getProfile?.()); // undefined

// Nullish Coalescing (??)

// Problem with || operator
let count = 0;
let value1 = count || 10;  // 10 (0 is falsy, uses default)
let value2 = count ?? 10;  // 0 (0 is not null/undefined)

let text = "";
let msg1 = text || "default";  // "default" ("" is falsy)
let msg2 = text ?? "default";  // "" (empty string is valid)

// Use ?? for actual null/undefined checks
let config = {
    timeout: 0,      // 0 is valid
    enabled: false,  // false is valid
    name: ""         // empty string is valid
};

let timeout = config.timeout ?? 3000;    // 0 (not null/undefined)
let enabled = config.enabled ?? true;    // false (not null/undefined)
let name = config.name ?? "Unnamed";     // "" (not null/undefined)
let missing = config.missing ?? "N/A";   // "N/A" (undefined)

// Combining ?. and ??
const data = {
    user: null,
    settings: { theme: "dark" }
};

let theme = data?.settings?.theme ?? "light";  // "dark"
let userName = data?.user?.name ?? "Guest";    // "Guest"

// Real-world example: API response
function getUserCity(response) {
    return response?.data?.user?.address?.city ?? "Unknown";
}

console.log(getUserCity(null));                    // "Unknown"
console.log(getUserCity({ data: null }));          // "Unknown"
console.log(getUserCity({
    data: { user: { address: { city: "NYC" } } }
})); // "NYC"

// Short-circuit evaluation
let expensiveFn = () => { console.log("Called"); return 42; };
let result = null?.property?.method?.();  // undefined (expensiveFn not called)
console.log(result);  // undefined
Warning: Don't overuse ?. as it can hide bugs. Use it for genuinely optional properties, not to mask errors in required data.

7. Logical Assignment Operators (||=, &&=, ??=)

Operator Name Equivalent Assigns When
||= ES2021 Logical OR Assignment x || (x = y) x is falsy
&&= ES2021 Logical AND Assignment x && (x = y) x is truthy
??= ES2021 Nullish Assignment x ?? (x = y) x is null or undefined
Operator Behavior Use Case Example
||= Assigns if current value is falsy Set default for any falsy value x ||= 10 (assigns if x is falsy)
&&= Assigns if current value is truthy Update existing truthy value x &&= 10 (assigns if x is truthy)
??= Assigns only if null/undefined Set default only for null/undefined x ??= 10 (assigns if x is null/undefined)

Before ES2021:

// Logical OR pattern
x = x || defaultValue;
if (!x) x = defaultValue;

// Logical AND pattern
x = x && newValue;
if (x) x = newValue;

// Nullish pattern
x = x ?? defaultValue;
if (x == null) x = defaultValue;

With ES2021:

// Logical OR assignment
x ||= defaultValue;


// Logical AND assignment
x &&= newValue;


// Nullish assignment
x ??= defaultValue;

Example: Logical assignment operators

// ||= Logical OR Assignment
// Assigns if left side is falsy
let a = 0;
a ||= 10;        // a is 10 (0 is falsy)

let b = 5;
b ||= 10;        // b is still 5 (5 is truthy)

let c = "";
c ||= "default"; // c is "default" ("" is falsy)

// &&= Logical AND Assignment
// Assigns if left side is truthy
let x = 5;
x &&= 10;        // x is 10 (5 is truthy)

let y = 0;
y &&= 10;        // y is still 0 (0 is falsy, no assignment)

let z = null;
z &&= 10;        // z is still null (null is falsy)

// ??= Nullish Assignment
// Assigns only if null or undefined
let p = 0;
p ??= 10;        // p is still 0 (0 is not null/undefined)

let q = null;
q ??= 10;        // q is 10 (null)

let r = undefined;
r ??= 10;        // r is 10 (undefined)

let s = false;
s ??= true;      // s is still false (false is not null/undefined)

// Practical use cases

// 1. Object property defaults with ??=
const config = {
    timeout: 0,      // 0 is valid
    retry: null      // null should be replaced
};

config.timeout ??= 3000;  // Still 0 (not null/undefined)
config.retry ??= 3;       // Now 3 (was null)
config.newProp ??= "default"; // "default" (was undefined)

console.log(config);
// { timeout: 0, retry: 3, newProp: "default" }

// 2. Accumulation with &&=
let total = 100;
let applyDiscount = true;

total &&= total * 0.9;  // Apply 10% discount if total exists
console.log(total);        // 90

let nullTotal = null;
nullTotal &&= nullTotal * 0.9;  // No operation (nullTotal is falsy)
console.log(nullTotal);    // null (unchanged)

// 3. Cache pattern with ||=
let cache = {};

function getData(key) {
    // Set cache if not already set
    cache[key] ||= expensiveOperation(key);
    return cache[key];
}

function expensiveOperation(key) {
    console.log("Computing...");
    return key * 2;
}

console.log(getData(5));   // "Computing..." then 10
console.log(getData(5));   // 10 (from cache, no computing)

// 4. Updating objects conditionally
const user = {
    name: "Alice",
    age: null,
    status: undefined
};

user.name ||= "Anonymous";    // Still "Alice" (truthy)
user.age ??= 18;              // Now 18 (was null)
user.status ??= "active";     // Now "active" (was undefined)

// 5. Short-circuit: right side not evaluated if condition not met
let count = 0;
let getValue = () => { count++; return 100; };

let x1 = 5;
x1 ||= getValue();  // getValue() NOT called (x1 is truthy)
console.log(count); // 0

let x2 = 0;
x2 ||= getValue();  // getValue() IS called (x2 is falsy)
console.log(count); // 1
Note: Logical assignment operators combine logical operations with assignment, providing cleaner syntax and short-circuit evaluation (right side only evaluated when assignment occurs).

Section 4 Summary

  • Arithmetic operators include +, -, *, /, %, ** (exponentiation); + concatenates strings if either operand is string
  • Assignment operators support compound forms (+=, -=, *=, etc.) for concise update operations
  • Use strict equality (===, !==) to avoid type coercion bugs; loose equality (==) performs type conversion
  • Logical operators (&&, ||, !) short-circuit and return actual values, not just booleans; useful for defaults and guards
  • Bitwise operators work on 32-bit integers; useful for flags, permissions, and fast math operations
  • Optional chaining (?.) safely accesses nested properties; nullish coalescing (??) provides defaults only for null/undefined
  • Logical assignment (||=, &&=, ??=) combines logic with assignment; short-circuits when assignment unnecessary