046: Expressions and Operators
Outcomes
By the end of this lesson, you can:
- build arithmetic and assignment expressions;
- compare values with relational operators and strict equality;
- combine Boolean rules with
&&,||, and!; - use common unary operators including
typeofand unary negation; - predict basic precedence and add parentheses for clarity; and
- identify coercion surprises without relying on them.
Prerequisites and Retrieval
Start by retrieving the values and types from 044.
- What are the types of
"12",12, andfalse? - How do you identify an array?
- Which declaration permits reassignment?
For today's examples, variables, values, and expressions are enough. Tomorrow, conditions will use the results these expressions produce.
Terms
- operator: A symbol that performs an operation on one or more operands, such as
+,&&, ortypeof. — Source: MDN: Operator - operand: A value that an operator acts on. — Source: MDN: Operator
- binary operator: An operator that requires two operands, as in
a + b. — Source: MDN: Operator - unary operator: An operator that takes one operand, as in
-x,!flag, ortypeof v. — Source: MDN: Operator - arithmetic operator: An operator that computes a numeric result:
+,-,*,/,%, or**. — Source: MDN: Arithmetic operators - assignment operator:
=and its compound forms, such as+=, which store values in bindings. — Source: MDN: Assignment operators - comparison operator: An operator that compares values:
===,!==,<,>,<=, or>=. — Source: MDN: Comparison operators - strict equality:
===compares both type and value without coercion. — Source: MDN: Strict equality - logical operator:
&&,||, and!combine or negate conditions using short-circuit evaluation. — Source: MDN: Logical AND - precedence: The order that determines which operator binds first in a mixed expression. — Source: MDN: Operator precedence
- coercion: Automatic conversion of a value from one type to another. — Source: MDN: Type coercion
- short-circuiting: Stopping evaluation as soon as the result is already known, as with
&&and||. — Source: MDN: Logical AND - Truthiness (official): "A value is truthy if it coerces to true in Boolean context; falsy values are false, 0, -0, 0n, "", null, undefined, NaN, document.all." — Source: MDN: Truthy
- Coercion (official): "Coercion is automatic conversion of a value from one type to another." — Source: MDN: Type coercion
- Nullish: "Nullish values are specifically null and undefined (used by ??)." — Source: MDN: Nullish coalescing
Beginner Explanation and Mental Model
Think of an operator as a verb inside an expression. In price * quantity, multiplication describes the operation JavaScript should perform on the two operands. The resulting value can then be stored, logged, compared, or passed into another expression.
Arithmetic
const sum = 10 + 4; // 14
const difference = 10 - 4; // 6
const product = 10 * 4; // 40
const quotient = 10 / 4; // 2.5
const remainder = 10 % 4; // 2
const power = 10 ** 2; // 100
% produces a remainder, which makes it useful for checks such as determining whether a number is even or odd. With numeric operands, division by zero produces Infinity rather than throwing an exception. Real calculations should still validate their divisors before dividing.
Assignment
= stores a value; it does not test whether two values are equal:
let stock = 10;
stock = 8;
stock += 2; // equivalent to stock = stock + 2
stock -= 1;
Compound assignment makes the intent to update a value visible. It works with a let binding or a mutable property, but it cannot reassign a const binding.
Comparison and strict equality
The relational operators >, <, >=, and <= compare ordering. === asks whether two values have the same type and the same value under JavaScript's strict equality rules. !== asks whether they do not.
console.log(10 >= 10); // true
console.log(7 < 3); // false
console.log(5 === 5); // true
console.log("5" === 5); // false
console.log("5" !== 5); // true
Use === and !== in course code. Loose equality, == and !=, performs type coercion and can produce results that are surprising when you are still building your mental model; for example, 0 == false is true. You will encounter that older syntax in existing code, but do not use it as a shortcut. Convert data deliberately, then compare it strictly.
Logical and unary operators
With Boolean operands, && means that both rules must be true, || means that at least one rule must be true, and ! reverses a truth value:
const hasTicket = true;
const isOnTime = true;
const canEnter = hasTicket && isOnTime;
const needsHelp = !hasTicket;
There is a detail that often causes confusion: in JavaScript, && and || return one of their operands, not necessarily a Boolean. For beginner business rules, keep the operands Boolean so the results are easy to reason about. These operators also short-circuit. false && secondExpression never evaluates secondExpression, and true || secondExpression likewise skips it.
The unary operator typeof reports a type string, unary - negates a number, and ! converts its operand to a Boolean before reversing it. For explicit number conversion, prefer Number(text) to the clever-looking unary +text form.
Precedence
Multiplication and division group before addition and subtraction. Comparisons group after arithmetic, and && groups before ||. You do not need to memorize an enormous precedence table. Parentheses are often the better choice because they show the business rule directly:
const total = (price * quantity) + fee;
const canBuy = isMember && (hasCredit || hasVoucher);
Worked Example: Order Calculation
const unitPrice = 250;
const quantity = 3;
const deliveryFee = 40;
const freeDeliveryMinimum = 700;
const subtotal = unitPrice * quantity;
const qualifiesForFreeDelivery = subtotal >= freeDeliveryMinimum;
const appliedDeliveryFee = qualifiesForFreeDelivery ? 0 : deliveryFee;
const total = subtotal + appliedDeliveryFee;
const isExactBudget = total === 750;
console.log("Subtotal:", subtotal);
console.log("Free delivery:", qualifiesForFreeDelivery);
console.log("Delivery fee:", appliedDeliveryFee);
console.log("Total:", total);
console.log("Exactly 750:", isExactBudget);
Expected output:
Subtotal: 750
Free delivery: true
Delivery fee: 0
Total: 750
Exactly 750: true
The conditional ? : expression selects one of two values. Conditions are covered fully tomorrow; here, the expression lets the arithmetic result drive a choice. The named intermediate values give you a useful debugging path: calculate the subtotal, compare it with the threshold, choose the delivery fee, and then calculate the total. That is easier to inspect than a single dense expression.
Intermediate Example: Access Rule
const age = 19;
const hasVerifiedId = true;
const isSuspended = false;
const hasUserPass = false;
const hasGuestPass = true;
const meetsAgeRule = age >= 18;
const hasAcceptedPass = hasUserPass || hasGuestPass;
const canAccess = meetsAgeRule && hasVerifiedId && !isSuspended && hasAcceptedPass;
console.log("Age rule:", meetsAgeRule);
console.log("Accepted pass:", hasAcceptedPass);
console.log("Not suspended:", !isSuspended);
console.log("Can access:", canAccess);
The expected output is four true results. Each named Boolean answers a separate question, which makes the final rule easier to diagnose. When hasUserPass is false, || continues to the guest pass. If the age rule were false, the later operands in the chained && would not be needed to determine the result.
Here is a coercion demonstration, not a recommendation:
console.log("10" + 2); // "102"
console.log("10" - 2); // 8
console.log("0" === 0); // false
The results look inconsistent because different operators apply different conversion rules. At an input boundary, convert the text deliberately with const amount = Number(inputText);, verify that the result is a valid number, and only then perform arithmetic.
Optional Advanced Extension: Nullish Defaults
?? uses its right operand only when the left operand is null or undefined:
const savedVolume = 0;
const defaultVolume = 50;
console.log(savedVolume || defaultVolume); // 50
console.log(savedVolume ?? defaultVolume); // 0
0 is a valid volume, but it is falsy, so || replaces it with the default. ?? preserves it because zero is not nullish. Keep ?? separate from && or || with parentheses. Parentheses are required in some mixtures and improve readability in all of them.
Deep Dive: Operator Families and Evaluation
Arithmetic and comparisons are only part of the operator system. Several other families appear regularly in production JavaScript.
Unary operators
typeof value;
!isReady;
Number("42");
delete cache.temp;
Use delete to remove an object property, not to remove an item from an array.
Logical operators return operands
const displayName = user.nickname || "Guest";
const exactName = user.nickname ?? "Guest";
|| falls back for every falsy value. ?? falls back only for null and undefined.
0 || 10; // 10
0 ?? 10; // 0
Logical assignment
settings.theme ??= "system";
cache.items ||= [];
isReady &&= hasPermission;
These operators combine a logical check with an assignment. Use them when that compressed form makes the intent clearer, not merely because it uses fewer characters.
Bitwise operators
Bitwise operators convert operands to 32-bit integers, except for their BigInt forms. They are useful for low-level flags, but they are uncommon in ordinary UI and business logic.
const READ = 1; // 001
const WRITE = 2; // 010
const DELETE = 4; // 100
const permission = READ | WRITE;
console.log((permission & WRITE) === WRITE); // true
BigInt operators
Most arithmetic and bitwise operators support BigInt values when both operands are BigInts.
10n ** 3n; // 1000n
15n / 4n; // 3n — integer division
Precedence is not a style contest
Knowing precedence does not mean you should make readers reconstruct it mentally. Parentheses can communicate intent more effectively:
const payable = subtotal + subtotal * taxRate - discount;
This may be clearer when expressed as:
const tax = subtotal * taxRate;
const payable = subtotal + tax - discount;
Readable intermediate names often communicate more than a compressed expression.
Mistakes and Debugging
- Using
=in a comparison:=assigns. Use===to compare. - Using
==to accommodate mismatched types: fix the type or convert it explicitly, then use===. - Expecting
+always to add: if either operand becomes a string, it may concatenate. Log both the value andtypeof. - Incorrect precedence: break a long expression into named values and add parentheses.
- Reassigning a
const: compound assignment still reassigns the binding. - Expecting logical operators always to return Booleans: use Boolean operands when expressing Boolean rules.
- Comparing
NaNwith===: useNumber.isNaN(value). - Floating-point surprise:
0.1 + 0.2is not exactly0.3in binary floating-point. Where appropriate, store money in the smallest whole units or use domain-safe decimal handling.
Best Practices
- Use
===and!==; avoid loose equality in application code. - Convert external strings explicitly with
Number()and validate the result. - Use named intermediate values for business rules.
- Parenthesize mixed logical conditions so the intent is obvious.
- Keep arithmetic operands numeric.
- Do not embed assignments inside conditions or chain assignments.
- Use
%for a remainder, not a percentage; calculate 15 percent asamount * 0.15. - Prefer readable expressions to operator tricks.
Tiered Exercises
Core
With const a = 17 and const b = 5, calculate all six arithmetic operations. Compare them with >, <, ===, and !==. Make your predictions before logging the results.
Practice
Calculate a cart subtotal from price and quantity. A purchase is eligible for checkout only when the subtotal is at least 500, stock is available, and the account is not blocked. Log each named rule as well as the final result.
Professional Extension
Given const input = "25", convert it explicitly to a number. Demonstrate strict comparison before and after conversion, calculate the doubled value, and explain why using the original value with + would be unsafe.
Complete Solutions
const a = 17;
const b = 5;
console.log(a + b); // 22
console.log(a - b); // 12
console.log(a * b); // 85
console.log(a / b); // 3.4
console.log(a % b); // 2
console.log(a ** b); // 1419857
console.log(a > b); // true
console.log(a < b); // false
console.log(a === b); // false
console.log(a !== b); // true
const price = 180;
const quantity = 3;
const hasStock = true;
const isBlocked = false;
const subtotal = price * quantity;
const meetsMinimum = subtotal >= 500;
const canCheckout = meetsMinimum && hasStock && !isBlocked;
console.log("Subtotal:", subtotal);
console.log("Meets minimum:", meetsMinimum);
console.log("Has stock:", hasStock);
console.log("Can checkout:", canCheckout);
Expected final result: Can checkout: true.
const input = "25";
const amount = Number(input);
console.log(input === 25); // false
console.log(amount === 25); // true
console.log(amount * 2); // 50
console.log(input + 2); // "252", demonstrating the risk
The conversion makes the numeric intent explicit. In real input handling, also check Number.isNaN(amount).
Recap and Exit Questions
Operators build values from operands. Arithmetic calculates, assignment stores, comparisons produce decisions, and logical operators combine rules. Strict equality avoids implicit conversion. Precedence controls grouping, while named steps and parentheses make the code easier to read and debug.
- How are
=and===different? - Why does
"5" === 5produce false? - What do
&&,||, and!mean with Booleans? - What does
%calculate? - How would you debug an unexpected
"102"result?
Official References
- MDN: Expressions and operators
- MDN: strict equality
- MDN: logical operators
- MDN: operator precedence
- ECMA-262: ECMAScript language expressions
References checked 2026-08-24.
