Part 3: Operators and Expressions
Combining Values to Produce Results
Expressions and Statements
Before diving into operators, let's clarify the difference between expressions and statements.
javascript// Expressions produce values 5 + 3 // 8 'Hello' + ' World' // 'Hello World' x > 10 ? 'big' : 'small' // produces a value // Statements perform actions let x = 10; // declaration statement if (x > 5) { } // control flow statement for (let i = 0; i < 10; i++) { } // loop statement // Expression statements - expressions used as statements console.log('hello'); // function call expression as statement x++; // increment expression as statement
Arithmetic Operators
Basic Arithmetic
javascript// Addition console.log(5 + 3); // 8 console.log('Hello' + ' ' + 'World'); // 'Hello World' (concatenation) // Subtraction console.log(10 - 4); // 6 // Multiplication console.log(6 * 7); // 42 // Division console.log(20 / 4); // 5 console.log(7 / 2); // 3.5 (not integer division!) // Modulo (remainder) console.log(10 % 3); // 1 console.log(17 % 5); // 2 console.log(-10 % 3); // -1 (sign follows dividend) // Exponentiation console.log(2 ** 10); // 1024 console.log(9 ** 0.5); // 3 (square root)
Unary Operators
javascript// Unary plus - converts to number console.log(+'42'); // 42 console.log(+true); // 1 console.log(+'hello'); // NaN // Unary minus - negates console.log(-5); // -5 console.log(-(-5)); // 5 // Increment and Decrement let x = 5; console.log(x++); // 5 (returns, then increments) console.log(x); // 6 let y = 5; console.log(++y); // 6 (increments, then returns) console.log(y); // 6 // Same for decrement let z = 5; console.log(z--); // 5 console.log(--z); // 3
Assignment Operators
javascriptlet 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 // Chained assignment (right to left) let a, b, c; a = b = c = 10; console.log(a, b, c); // 10 10 10
Comparison Operators
Equality Operators
javascript// Strict equality (===) - no type coercion console.log(5 === 5); // true console.log(5 === '5'); // false console.log(null === undefined); // false // Strict inequality (!==) console.log(5 !== '5'); // true console.log(5 !== 5); // false // Loose equality (==) - allows type coercion console.log(5 == '5'); // true (string converted to number) console.log(0 == false); // true console.log(null == undefined); // true console.log('' == 0); // true console.log([] == false); // true (empty array → '' → 0) // Loose inequality (!=) console.log(5 != '5'); // false
Relational Operators
javascript// Greater than / Less than console.log(5 > 3); // true console.log(5 < 3); // false // Greater than or equal / Less than or equal console.log(5 >= 5); // true console.log(5 <= 4); // false // String comparison (lexicographic) console.log('apple' < 'banana'); // true console.log('Apple' < 'apple'); // true (uppercase comes first) console.log('2' > '10'); // true (string comparison!) console.log(2 > 10); // false (number comparison)
Special Comparisons
javascript// NaN is not equal to anything, including itself console.log(NaN === NaN); // false console.log(Number.isNaN(NaN)); // true // Object comparison (by reference) const obj1 = { value: 1 }; const obj2 = { value: 1 }; const obj3 = obj1; console.log(obj1 === obj2); // false (different objects) console.log(obj1 === obj3); // true (same reference) // Object.is for more precise comparison console.log(Object.is(NaN, NaN)); // true console.log(Object.is(0, -0)); // false console.log(0 === -0); // true
Logical Operators
Basic Logical Operators
javascript// AND (&&) console.log(true && true); // true console.log(true && false); // false console.log(false && true); // false console.log(false && false); // false // OR (||) console.log(true || true); // true console.log(true || false); // true console.log(false || true); // true console.log(false || false); // false // NOT (!) console.log(!true); // false console.log(!false); // true console.log(!!0); // false (double NOT for boolean conversion) console.log(!!'hello'); // true
Short-Circuit Evaluation
javascript// AND short-circuits on first falsy value console.log(false && 'hello'); // false console.log(true && 'hello'); // 'hello' console.log('a' && 'b' && 'c'); // 'c' (last value if all truthy) console.log('a' && 0 && 'c'); // 0 (first falsy value) // OR short-circuits on first truthy value console.log(true || 'hello'); // true console.log(false || 'hello'); // 'hello' console.log(null || 0 || 'hi'); // 'hi' (first truthy value) console.log(null || 0 || ''); // '' (last value if all falsy)
Practical Uses of Short-Circuit
javascript// Default values (traditional way) function greet(name) { name = name || 'Guest'; console.log(`Hello, ${name}!`); } greet(); // 'Hello, Guest!' greet('Alice'); // 'Hello, Alice!' // Conditional execution const isLoggedIn = true; isLoggedIn && console.log('Welcome back!'); // Guard against null/undefined const user = null; const name = user && user.name; // undefined (doesn't throw) // Object property access const config = {}; const timeout = config.settings && config.settings.timeout;
Nullish Coalescing (??)
javascript// ?? returns right side only for null/undefined console.log(null ?? 'default'); // 'default' console.log(undefined ?? 'default'); // 'default' console.log(0 ?? 'default'); // 0 (0 is not nullish) console.log('' ?? 'default'); // '' (empty string is not nullish) console.log(false ?? 'default'); // false // Comparison with || console.log(0 || 'default'); // 'default' (0 is falsy) console.log(0 ?? 'default'); // 0 (0 is not nullish) // Practical use function getConfig(options) { const timeout = options.timeout ?? 5000; // Default only if null/undefined const retries = options.retries ?? 3; return { timeout, retries }; } getConfig({ timeout: 0, retries: 0 }); // { timeout: 0, retries: 0 }
The Conditional (Ternary) Operator
javascript// Syntax: condition ? valueIfTrue : valueIfFalse const age = 20; const status = age >= 18 ? 'adult' : 'minor'; console.log(status); // 'adult' // Nested ternary (use sparingly) const score = 85; const grade = score >= 90 ? 'A' : score >= 80 ? 'B' : score >= 70 ? 'C' : score >= 60 ? 'D' : 'F'; console.log(grade); // 'B' // In template literals const count = 5; console.log(`You have ${count} item${count === 1 ? '' : 's'}`); // 'You have 5 items' // For conditional assignment const config = { debug: process.env.NODE_ENV !== 'production' ? true : false }; // Simplified (when returning boolean) const config2 = { debug: process.env.NODE_ENV !== 'production' };
Bitwise Operators
Bitwise operators work on 32-bit integers.
Basic Bitwise Operators
javascript// AND (&) console.log(5 & 3); // 1 // 5 = 101 // 3 = 011 // & = 001 = 1 // OR (|) console.log(5 | 3); // 7 // 5 = 101 // 3 = 011 // | = 111 = 7 // XOR (^) console.log(5 ^ 3); // 6 // 5 = 101 // 3 = 011 // ^ = 110 = 6 // NOT (~) console.log(~5); // -6 // ~n = -(n + 1) // Left shift (<<) console.log(5 << 1); // 10 // 5 = 101 // << = 1010 = 10 (multiply by 2) // Right shift (>>) console.log(5 >> 1); // 2 // 5 = 101 // >> = 10 = 2 (divide by 2, floor) // Unsigned right shift (>>>) console.log(-1 >>> 0); // 4294967295
Practical Bitwise Uses
javascript// Check if number is even/odd const isEven = n => (n & 1) === 0; console.log(isEven(4)); // true console.log(isEven(7)); // false // Swap values without temp variable let a = 5, b = 3; a ^= b; b ^= a; a ^= b; console.log(a, b); // 3 5 // Multiply/divide by powers of 2 console.log(5 << 2); // 20 (5 * 4) console.log(20 >> 2); // 5 (20 / 4) // Floor a number console.log(~~3.7); // 3 console.log(3.7 | 0); // 3 // Check flags const READ = 1; // 001 const WRITE = 2; // 010 const EXECUTE = 4; // 100 let permissions = READ | WRITE; // 011 // Check permission console.log((permissions & READ) !== 0); // true console.log((permissions & EXECUTE) !== 0); // false // Add permission permissions |= EXECUTE; // 111 // Remove permission permissions &= ~WRITE; // 101
Optional Chaining (?.)
javascriptconst user = { name: 'Alice', address: { city: 'Boston' } }; // Without optional chaining const zipCode = user.address && user.address.zipCode; // With optional chaining const zipCode2 = user.address?.zipCode; // undefined (doesn't throw) // Deeply nested access const street = user?.address?.street?.name; // undefined // With arrays const arr = null; const first = arr?.[0]; // undefined // With function calls const user2 = { getName: () => 'Alice' }; console.log(user2.getName?.()); // 'Alice' console.log(user2.getAge?.()); // undefined // Combined with nullish coalescing const city = user?.address?.city ?? 'Unknown'; console.log(city); // 'Boston'
The typeof Operator
javascriptconsole.log(typeof undefined); // 'undefined' console.log(typeof null); // 'object' (bug) console.log(typeof true); // 'boolean' console.log(typeof 42); // 'number' console.log(typeof 42n); // 'bigint' console.log(typeof 'hello'); // 'string' console.log(typeof Symbol()); // 'symbol' console.log(typeof {}); // 'object' console.log(typeof []); // 'object' console.log(typeof function(){}); // 'function' // Safe undefined check if (typeof myVar === 'undefined') { // myVar doesn't exist or is undefined } // Type checking pattern function process(value) { switch (typeof value) { case 'string': return value.toUpperCase(); case 'number': return value * 2; case 'boolean': return !value; default: return value; } }
The instanceof Operator
javascript// Check if object is instance of constructor const arr = [1, 2, 3]; console.log(arr instanceof Array); // true console.log(arr instanceof Object); // true (arrays are objects) const date = new Date(); console.log(date instanceof Date); // true // Custom classes class Animal {} class Dog extends Animal {} const dog = new Dog(); console.log(dog instanceof Dog); // true console.log(dog instanceof Animal); // true console.log(dog instanceof Object); // true // Primitives console.log('hello' instanceof String); // false console.log(new String('hello') instanceof String); // true
The in Operator
javascript// Check if property exists in object const obj = { name: 'Alice', age: 30 }; console.log('name' in obj); // true console.log('email' in obj); // false console.log('toString' in obj); // true (inherited) // Check array indices const arr = [1, 2, 3]; console.log(0 in arr); // true console.log(5 in arr); // false console.log('length' in arr); // true
Operator Precedence
Operators have different precedence levels. Higher precedence operators execute first.
Precedence (highest to lowest): ┌────┬──────────────────────────────────────────┐ │ 1 │ () grouping │ ├────┼──────────────────────────────────────────┤ │ 2 │ . [] () ?. member access, call │ ├────┼──────────────────────────────────────────┤ │ 3 │ new (with args) │ ├────┼──────────────────────────────────────────┤ │ 4 │ ++ -- (postfix) │ ├────┼──────────────────────────────────────────┤ │ 5 │ ! ~ + - ++ -- typeof void delete (unary) │ ├────┼──────────────────────────────────────────┤ │ 6 │ ** │ ├────┼──────────────────────────────────────────┤ │ 7 │ * / % │ ├────┼──────────────────────────────────────────┤ │ 8 │ + - │ ├────┼──────────────────────────────────────────┤ │ 9 │ << >> >>> │ ├────┼──────────────────────────────────────────┤ │ 10 │ < <= > >= in instanceof │ ├────┼──────────────────────────────────────────┤ │ 11 │ == != === !== │ ├────┼──────────────────────────────────────────┤ │ 12 │ & │ ├────┼──────────────────────────────────────────┤ │ 13 │ ^ │ ├────┼──────────────────────────────────────────┤ │ 14 │ | │ ├────┼──────────────────────────────────────────┤ │ 15 │ && │ ├────┼──────────────────────────────────────────┤ │ 16 │ || │ ├────┼──────────────────────────────────────────┤ │ 17 │ ?? │ ├────┼──────────────────────────────────────────┤ │ 18 │ ?: (ternary) │ ├────┼──────────────────────────────────────────┤ │ 19 │ = += -= *= etc. (assignment) │ ├────┼──────────────────────────────────────────┤ │ 20 │ , (comma) │ └────┴──────────────────────────────────────────┘
javascript// Precedence examples console.log(2 + 3 * 4); // 14 (not 20) console.log((2 + 3) * 4); // 20 console.log(true || false && false); // true // && has higher precedence: true || (false && false) console.log(5 > 3 === true); // true // > evaluates first: (5 > 3) === true // Use parentheses for clarity const result = (a > b) && (c < d); const value = condition ? (a + b) : (c + d);
Summary
Arithmetic operators:
+,-,*,/,%,**for math operations++,--for increment/decrement+=,-=, etc. for compound assignment
Comparison operators:
- Always prefer
===and!==over==and!= - Be careful with object comparison (by reference)
- Use
Object.is()for special cases likeNaN
Logical operators:
&&,||,!for logical operations- Short-circuit evaluation enables patterns like default values
??is safer than||for default values (nullish only)
Other operators:
?.for safe property accesstypeoffor type checkinginstanceoffor constructor checkinginfor property existence
Questions to Think About
- Why should you prefer
===over==?
Loose equality (
==) performs type coercion, which can lead to unexpected results like 0 == '' being true. Strict equality (===) requires both type and value to match, making your code more predictable and easier to debug.- When would you use
??instead of||?
Use
?? when you want to provide a default only for null or undefined, but want to keep other falsy values like 0, '', or false. Use || when any falsy value should trigger the default.- How does short-circuit evaluation work?
&& returns the first falsy value or the last value if all are truthy. || returns the first truthy value or the last value if all are falsy. Expressions after the first decisive value are never evaluated.- What are practical uses for bitwise operators?
Permission flags, checking even/odd numbers, fast multiplication/division by powers of 2, converting floats to integers, and low-level data manipulation.
- Why does operator precedence matter?
Without understanding precedence, expressions like
2 + 3 * 4 might seem to produce 20 instead of 14. Use parentheses to make intent clear and avoid bugs.Next: Part 4 - Control Flow, where we explore conditionals, loops, switch statements, and flow control.