➕ Operators

Performing Operations on Values

JavaScript Operators

Operators allow you to perform operations on variables and values. JavaScript provides arithmetic, comparison, logical, assignment, and many other operators.

🔢 Arithmetic Operators

// Basic arithmetic
let a = 10;
let 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.3333... (Division)
console.log(a % b);   // 1  (Modulus - remainder)
console.log(a ** b);  // 1000 (Exponentiation - ES2016)

// Increment and decrement
let x = 5;
x++;        // Post-increment: x = 6
++x;        // Pre-increment: x = 7
x--;        // Post-decrement: x = 6
--x;        // Pre-decrement: x = 5

// Difference between pre and post
let y = 5;
console.log(y++);  // 5 (returns then increments)
console.log(y);    // 6

let z = 5;
console.log(++z);  // 6 (increments then returns)
console.log(z);    // 6

// Unary operators
console.log(+true);   // 1  (converts to number)
console.log(+'5');    // 5
console.log(-'5');    // -5
console.log(+'hello'); // NaN

➕ Assignment Operators

// Basic assignment
let x = 10;

// Compound assignment
x += 5;   // x = x + 5  → 15
x -= 3;   // x = x - 3  → 12
x *= 2;   // x = x * 2  → 24
x /= 4;   // x = x / 4  → 6
x %= 4;   // x = x % 4  → 2
x **= 3;  // x = x ** 3 → 8

// Logical assignment (ES2021)
let a;
a ||= 10;  // a = a || 10 (if falsy, assign 10)
console.log(a);  // 10

let b = 0;
b ||= 5;   // b becomes 5 (0 is falsy)

let c = null;
c ??= 10;  // c = c ?? 10 (if null/undefined, assign 10)
console.log(c);  // 10

let d = false;
d &&= true;  // d = d && true (if truthy, assign true)
console.log(d);  // false (d was falsy)

⚖️ Comparison Operators

// Equality
console.log(5 == 5);      // true  (loose equality)
console.log(5 == '5');    // true  (converts types)
console.log(5 === 5);     // true  (strict equality)
console.log(5 === '5');   // false (different types)
console.log(5 != '6');    // true  (loose inequality)
console.log(5 !== '5');   // true  (strict inequality)

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

// String comparison (lexicographical)
console.log('apple' < 'banana');   // true
console.log('Z' < 'a');            // true (uppercase < lowercase)
console.log('10' < '2');           // true (string comparison!)
console.log('10' < 2);             // false (converts to number)

// Comparing different types
console.log('5' > 3);     // true (converts '5' to 5)
console.log(null == undefined);  // true
console.log(null === undefined); // false
console.log(NaN === NaN); // false (NaN is not equal to anything!)
console.log(Object.is(NaN, NaN)); // true (ES6 method)

🔗 Logical Operators

AND (&&)

// Returns first falsy value or last value
console.log(true && true);        // true
console.log(true && false);       // false
console.log(false && true);       // false

// Short-circuit evaluation
console.log(5 && 10);             // 10 (both truthy, returns last)
console.log(0 && 10);             // 0 (first falsy)
console.log(null && 'hello');     // null (first falsy)
console.log('hello' && 'world');  // 'world' (both truthy)

// Practical usage
let user = { name: 'John', age: 30 };
console.log(user && user.name);   // 'John'

user = null;
console.log(user && user.name);   // null (prevents error)

OR (||)

// Returns first truthy value or last value
console.log(true || false);       // true
console.log(false || false);      // false
console.log(false || true);       // true

// Short-circuit evaluation
console.log(5 || 10);             // 5 (first truthy)
console.log(0 || 10);             // 10 (0 is falsy)
console.log(null || 'default');   // 'default'
console.log('first' || 'second'); // 'first'

// Default values
function greet(name) {
    name = name || 'Guest';
    return `Hello, ${name}!`;
}
console.log(greet());         // 'Hello, Guest!'
console.log(greet('John'));   // 'Hello, John!'

// Chain for fallbacks
let value = null || undefined || 0 || 'found';
console.log(value);  // 'found'

NOT (!)

// Inverts boolean value
console.log(!true);    // false
console.log(!false);   // true

// Convert to boolean
console.log(!0);       // true
console.log(!1);       // false
console.log(!'');      // true
console.log(!'hello'); // false

// Double NOT for boolean conversion
console.log(!!0);       // false
console.log(!!1);       // true
console.log(!!'hello'); // true
console.log(!!'');      // false

// Practical usage
let isLoggedIn = false;
if (!isLoggedIn) {
    console.log('Please log in');
}

Nullish Coalescing (??)

// Returns right side only if left is null or undefined
// Unlike ||, it doesn't treat 0, '', false as falsy

console.log(null ?? 'default');      // 'default'
console.log(undefined ?? 'default'); // 'default'
console.log(0 ?? 'default');         // 0
console.log('' ?? 'default');        // ''
console.log(false ?? 'default');     // false

// Difference from ||
console.log(0 || 100);     // 100 (0 is falsy)
console.log(0 ?? 100);     // 0 (0 is not null/undefined)

console.log('' || 'text'); // 'text'
console.log('' ?? 'text'); // ''

// Practical usage
function getConfig(userConfig) {
    return {
        timeout: userConfig?.timeout ?? 5000,  // Default 5000
        retries: userConfig?.retries ?? 3      // Default 3
    };
}

console.log(getConfig({ timeout: 0 }));
// { timeout: 0, retries: 3 } - 0 is kept!

🔀 Conditional (Ternary) Operator

// Syntax: condition ? valueIfTrue : valueIfFalse

let age = 20;
let status = age >= 18 ? 'adult' : 'minor';
console.log(status);  // 'adult'

// Instead of if-else
let score = 85;
let grade;
if (score >= 90) {
    grade = 'A';
} else {
    grade = 'B';
}

// Can be written as:
grade = score >= 90 ? 'A' : 'B';

// Nested ternary (use sparingly!)
let num = 0;
let result = num > 0 ? 'positive' :
             num < 0 ? 'negative' :
             'zero';
console.log(result);  // 'zero'

// In function returns
function getStatus(age) {
    return age >= 18 ? 'adult' : 'minor';
}

// With JSX/React
const Greeting = ({ name }) => (
    name ? `Hello, ${name}!` : 'Hello, Guest!'
);

🔤 String Operators

// Concatenation with +
let first = 'Hello';
let second = 'World';
console.log(first + ' ' + second);  // 'Hello World'

// Template literals (preferred)
let name = 'John';
let age = 30;
console.log(`${name} is ${age} years old`);

// Concatenation with +=
let message = 'Hello';
message += ' ';
message += 'World';
console.log(message);  // 'Hello World'

// Number to string conversion
console.log('Score: ' + 100);  // 'Score: 100'
console.log(100 + 50 + ' points');  // '150 points'
console.log('Points: ' + 100 + 50); // 'Points: 10050' (!)

// Be careful with mixed operations
console.log(1 + 2 + '3');  // '33' (1+2=3, then '3'+'3')
console.log('1' + 2 + 3);  // '123' (all concatenation)

🎯 Type Operators

typeof

// Returns string describing type
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 bug)
console.log(typeof {});           // 'object'
console.log(typeof []);           // 'object'
console.log(typeof function(){}); // 'function'
console.log(typeof Symbol('id')); // 'symbol'

// Practical usage
function add(a, b) {
    if (typeof a !== 'number' || typeof b !== 'number') {
        throw new Error('Both arguments must be numbers');
    }
    return a + b;
}

instanceof

// Checks if object is instance of a class
let arr = [1, 2, 3];
let date = new Date();
let obj = {};

console.log(arr instanceof Array);    // true
console.log(date instanceof Date);    // true
console.log(obj instanceof Object);   // true
console.log(arr instanceof Object);   // true (Array extends Object)

// With custom classes
class Person {
    constructor(name) {
        this.name = name;
    }
}

let john = new Person('John');
console.log(john instanceof Person);  // true
console.log(john instanceof Object);  // true

// Not for primitives
console.log('hello' instanceof String);  // false
console.log(5 instanceof Number);        // false

in

// Checks if property exists in object
let person = {
    name: 'John',
    age: 30
};

console.log('name' in person);     // true
console.log('email' in person);    // false
console.log('toString' in person); // true (inherited)

// With arrays (checks index)
let colors = ['red', 'green', 'blue'];
console.log(0 in colors);  // true
console.log(3 in colors);  // false

// Difference from hasOwnProperty
console.log(person.hasOwnProperty('name'));      // true
console.log(person.hasOwnProperty('toString'));  // false (inherited)

🔢 Bitwise Operators

// Operate on 32-bit binary representations
console.log(5 & 1);    // 1  (AND)
console.log(5 | 1);    // 5  (OR)
console.log(5 ^ 1);    // 4  (XOR)
console.log(~5);       // -6 (NOT)
console.log(5 << 1);   // 10 (Left shift)
console.log(5 >> 1);   // 2  (Right shift)
console.log(-5 >>> 1); // 2147483645 (Unsigned right shift)

// Practical uses

// Check if number is even
function isEven(num) {
    return (num & 1) === 0;
}

// Multiply by 2
function double(num) {
    return num << 1;
}

// Divide by 2
function half(num) {
    return num >> 1;
}

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

📊 Operator Precedence

// Operators have different precedence levels
console.log(2 + 3 * 4);     // 14 (not 20) - * before +
console.log((2 + 3) * 4);   // 20 (parentheses first)

// Order (highest to lowest):
// 1. Grouping: ()
// 2. Member access: . []
// 3. Call/Create: () new
// 4. Postfix: ++ --
// 5. Prefix: ++ -- ! typeof + -
// 6. Exponentiation: **
// 7. Multiply/Divide: * / %
// 8. Add/Subtract: + -
// 9. Shift: << >> >>>
// 10. Relational: < <= > >= in instanceof
// 11. Equality: == != === !==
// 12. Bitwise AND: &
// 13. Bitwise XOR: ^
// 14. Bitwise OR: |
// 15. Logical AND: &&
// 16. Logical OR: ||
// 17. Conditional: ?:
// 18. Assignment: = += -= *= /= etc.
// 19. Comma: ,

// Examples
let result = 10 + 5 * 2;      // 20 (5*2=10, 10+10=20)
let value = 10 > 5 && 20 > 15; // true
let mixed = 3 + 4 * 5 / 3;    // 9.666... (4*5=20, 20/3=6.67, 3+6.67=9.67)

// Use parentheses for clarity!
let clear = (3 + 4) * (5 / 3);

💡 Practical Examples

Safe Navigation

// Optional chaining (?.) - ES2020
let user = {
    name: 'John',
    address: {
        city: 'New York'
    }
};

console.log(user?.address?.city);    // 'New York'
console.log(user?.contact?.phone);   // undefined (no error!)

// With arrays
let users = [{ name: 'John' }];
console.log(users?.[0]?.name);       // 'John'
console.log(users?.[5]?.name);       // undefined

// With functions
let greet = null;
console.log(greet?.());              // undefined (no error!)

greet = () => 'Hello';
console.log(greet?.());              // 'Hello'

Smart Defaults

function createUser(options = {}) {
    return {
        name: options.name ?? 'Anonymous',
        age: options.age ?? 18,
        role: options.role || 'user',
        isActive: options.isActive ?? true
    };
}

console.log(createUser());
// { name: 'Anonymous', age: 18, role: 'user', isActive: true }

console.log(createUser({ name: 'John', age: 0 }));
// { name: 'John', age: 0, role: 'user', isActive: true }
// age: 0 is kept because of ??

Value Validation

function validateInput(value) {
    // Check existence
    if (value === null || value === undefined) {
        return 'Value is required';
    }
    
    // Type check
    if (typeof value !== 'string') {
        return 'Value must be a string';
    }
    
    // Length check
    if (value.length < 3 || value.length > 20) {
        return 'Value must be between 3 and 20 characters';
    }
    
    return 'Valid';
}

console.log(validateInput('John'));    // 'Valid'
console.log(validateInput('Jo'));      // 'Value must be...'
console.log(validateInput(123));       // 'Value must be a string'

Flag Toggling

let isDarkMode = false;

// Toggle with !
isDarkMode = !isDarkMode;
console.log(isDarkMode);  // true

isDarkMode = !isDarkMode;
console.log(isDarkMode);  // false

// Toggle in object
let settings = { darkMode: false };
settings.darkMode = !settings.darkMode;

// Multiple flags with bitwise
const READ = 1;    // 001
const WRITE = 2;   // 010
const EXECUTE = 4; // 100

let permissions = READ | WRITE;  // 011 (3)
console.log(permissions & READ);    // 1 (has read)
console.log(permissions & EXECUTE); // 0 (no execute)

permissions |= EXECUTE;  // Add execute
console.log(permissions);  // 7 (111)

🎯 Key Takeaways