Operators and expressions
Language foundations14 min readLesson 3 of 4
3.1 Expressions and values
An expression is a combination of values, names and operators that produces a value. A statement does something with that value: assigns it, logs it, returns it, or uses it to decide a branch. Most lines of JavaScript are expressions wrapped in a statement.
An operator takes one or more operands. It returns a new value, and an operator call never changes an operand when the operand is a primitive. Compound assignment operators break that rule by assigning the result back to the variable on the left.
3.2 Arithmetic operators
The arithmetic set is +, -, *, /, % for remainder and ** for exponent. Division always produces a floating point result, so 10 / 4 is 2.5. The remainder operator follows the sign of the left operand, and the exponent operator binds tighter than multiplication.
Precedence is the usual maths order: exponent, then multiplication and division, then addition and subtraction. Parentheses are always allowed and often clearer than remembering a rule. A reader should not need a precedence table to follow one line.
console.log(7 + 3 * 2);
console.log((7 + 3) * 2);
console.log(17 % 5);
console.log(2 ** 5);
console.log(10 / 4);Output
13 20 2 32 2.5
The increment and decrement operators ++ and -- change a variable by one. Prefix and postfix forms differ in the value the surrounding expression sees: counter++ returns the old value and then increments, while ++counter increments first. Write the separate statement counter += 1 unless the compressed form clearly helps.
3.3 Comparison operators
Comparisons return a boolean. === and !== are strict, <, <=, > and >= compare numbers or strings. Two strings compare by Unicode code unit order, which is why uppercase letters sort before lowercase ones. Use localeCompare when alphabetical order for a human matters.
There is no chained comparison. Writing 0 < score < 100 evaluates 0 < score to a boolean and then compares that boolean with 100, which is almost never the intended test. Join two comparisons with &&.
console.log(10 > 3);
console.log("apple" < "banana");
console.log(3 !== "3");
console.log("Z" < "a");Output
true true true true
3.4 Unary operators
A unary operator takes one operand. typeof returns the type name, ! converts to boolean and negates, and void evaluates an expression and returns undefined. The unary minus converts its operand to a number and flips the sign, while unary plus converts to a number without changing it.
The plus sign is a compact conversion: +"42" is 42 and +"nope" is NaN. It is common in code golf and confusing in a code review. Use Number(...) when the conversion is the point of the line, and reserve unary plus for arithmetic where the type is already known.
delete removes a property from an object. It does not remove an array element in a useful way, because the array keeps its length and gains a hole. Use splice to remove an array item and delete only for object properties you truly want gone.
3.5 Logical operators
&& returns its first falsy operand or its last truthy operand. || returns its first truthy operand or its last falsy operand. ! converts to boolean and negates it. The return values are operands, not necessarily booleans, which makes short-circuiting useful for default values.
The nullish coalescing operator ?? falls back only when the left side is null or undefined. It differs from ||, which also falls back for 0, "" and false. A settings object that allows "0" or false needs ??, not ||.
const username = "";
const displayName = username || "Guest";
const zero = 0;
const safe = zero ?? 10;
const notSafe = zero || 10;
console.log(displayName);
console.log(safe);
console.log(notSafe);Output
Guest 0 10
Both && and || short circuit: the right operand runs only when the left did not decide the result. Code often relies on this for guards such as user && user.name, and modern code writes the same guard with optional chaining: user?.name. Optional chaining returns undefined instead of throwing when a link is null or undefined.
3.6 Assignment and the conditional operator
Assignment operators combine an operation with the assignment: +=, -=, *=, /=, ??=, ||= and &&=. They read as one operation and avoid repeating a long property name.
The conditional operator takes three operands: condition ? valueWhenTrue : valueWhenFalse. Use it for a short choice that returns a value. For more than one condition, an if statement is easier to scan and debug.
let points = 5;
points += 3;
points *= 2;
const label = points > 15 ? "high" : "low";
console.log(points);
console.log(label);Output
16 high
3.7 Precedence and readability
Precedence decides which operator runs first when one expression holds several of them. The rough order from tightest to loosest is: member access and calls, exponentiation, unary operators, multiplication and division, addition and subtraction, comparisons, equality, logical AND, logical OR and nullish, assignment, comma.
Remembering the whole table is less useful than recognising the lines that need brackets. A condition that mixes && and || should almost always use parentheses, even when precedence would do the right thing, because the reader should not have to rebuild the table. Assignment is looser than comparison, which explains the classic if (value = 3) mistake: the assignment runs first and the condition receives 3.
Bitwise operators &, |, ^, <<, >> and >>> work on 32-bit integers. They are useful for flags and low-level tasks and rare in ordinary application code. When they appear, comment the intent, because the symbols are easy to confuse with the logical operators.
3.8 Optional chaining and nullish assignment
Optional chaining ?. stops an expression when the value before it is null or undefined and returns undefined. user?.address?.city reads a nested property without a chain of if checks. It combines with calls and bracket access: items?.[0] and callback?.().
Nullish assignment ??= writes a value only when the target is null or undefined. It extends the ?? rule to an existing variable. The pair ||= writes for every falsy value, including 0 and the empty string, so choose according to whether those values are legitimate.
Optional chaining is not a substitute for validation. A missing property that should exist is still a bug, and returning undefined quietly can move the failure later. Use it at the edge where data arrives, then make the rest of the code work with a known shape.
3.9 Common mistakes
- Writing
=instead of===inside a condition. - Comparing strings with
<and expecting dictionary order across mixed case. - Chaining comparisons and trusting the result.
- Using
||for defaults when0orfalseare valid values. - Mixing many operators in one line instead of naming intermediate results.
- Relying on
++inside a larger expression.
Exam
Expression questions reward patience. Mark the operands and their types, apply precedence, and compute each subexpression on paper. If a logical operator appears, remember that it returns an operand, not always a boolean.
- Rewrite `7 + 3 * 2` with parentheses so the result is 20.
- Predict `"5" + 2` and `"5" - 2` before running them.
- Replace one `||` default with `??` and find the input where the result changes.
- Write a guard with `&&` and rewrite it with optional chaining.
